システム・プログラムI
                                       電子・情報工学系
                                       新城 靖
                                       <yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
	http://www.hlla.is.tsukuba.ac.jp/~yas/coins/syspro1-1998/1998-05-19
あるいは、次のページから手繰っていくこともできます。
	http://www.hlla.is.tsukuba.ac.jp/~yas/coins/
	http://www.hlla.is.tsukuba.ac.jp/~yas/index-j.html
レポートは、yas ではなくsyspro へ。
Subject: には、report1, report2 のように、番号を振るように。 report0 は不可。
プログラムを書く時に、実際に動く小さいプログラムの断片は、便利である。 日頃から自分で収集するといい。
コマンドがどんなどのシステム・コールを使っているかは、マニュアルの末尾 の SEE ALSO に出ている。
バッファ・オーバーランのバグ。
たとえば、100 バイトのメモリ領域しか割り当てていない時に、それ以上のメ モリを利用してしまうこと。インターネットからアクセスされるプログラムで は、このバグが狙われる。gets() や scanf(), sscanf() での %s、strcpy() などが危険。バイト数をチェックしない。
Java では、この問題は起きない。
----------------------------------------------------------------------
   1:	/*
   2:	        get-time.c -- 現在の時刻を表示するプログラム。
   3:	        ~yas/syspro1/time/get-time.c
   4:	        $Header: /home/lab2/OS/yas/syspro1/time/RCS/get-time.c,v 1.2 1998/05/18 14:18:20 yas Exp $
   5:	        Start: 1998/05/18 22:29:17
   6:	*/
   7:	
   8:	#include <sys/types.h>  /* time(2) */
   9:	#include <time.h>       /* time(2) */
  10:	
  11:	/*
  12:	typedef long    time_t;
  13:	time_t time(time_t *tloc);
  14:	*/
  15:	
  16:	main()
  17:	{
  18:	    time_t t ;
  19:	        t = time( 0 );
  20:	        printf("%d: %s",t,ctime(&t) );
  21:	        t ++ ;
  22:	        printf("%d: %s",t,ctime(&t) );
  23:	        t = 0 ;
  24:	        printf("%d: %s",t,ctime(&t) );
  25:	}
----------------------------------------------------------------------
環境変数 TZ で、ctime(3) ライブラリ関数の動きが変わる。---------------------------------------------------------------------- % date1998年 5月18日(月曜日) 22時40分33秒 JST % ./get-time
895498835: Mon May 18 22:40:35 1998 895498836: Mon May 18 22:40:36 1998 0: Thu Jan 1 09:00:00 1970 % printenv TZ
JST-9 % unsetenv TZ
% date
1998年 5月18日(月曜日) 13時41分03秒 GMT % ./get-time
895498869: Mon May 18 13:41:09 1998 895498870: Mon May 18 13:41:10 1998 0: Thu Jan 1 00:00:00 1970 %
----------------------------------------------------------------------
----------------------------------------------------------------------
   1:	/*
   2:	        proc-uid-print.c -- 現在のプロセスのUIDを表示するプログラム。
   3:	        ~yas/syspro1/proc/proc-uid-print.c
   4:	        $Header: /home/lab2/OS/yas/syspro1/user/RCS/uid-print.c,v 1.2 1998/05/18 14:43:10 yas Exp $
   5:	        Start: 1998/05/18 23:20:16
   6:	*/
   7:	
   8:	#include <sys/types.h>  /* getuid(2) */
   9:	#include <unistd.h>     /* getuid(2) */
  10:	#include <pwd.h>        /* getpwuid(3) */
  11:	#include <grp.h>        /* getgrgid(3) */
  12:	
  13:	#if     0
  14:	typedef o_uid_t o_gid_t;                /* old GID type         */
  15:	typedef long            uid_t;
  16:	extern uid_t getuid(void);
  17:	#endif
  18:	
  19:	extern  char *uid2uname(uid_t uid);
  20:	extern  char *gid2gname(gid_t gid);
  21:	
  22:	main()
  23:	{
  24:	    uid_t uid ;
  25:	        uid = getuid();
  26:	        printf("%d: %s\n",uid,uid2uname(uid) );
  27:	        uid = 0 ;
  28:	        printf("%d: %s\n",uid,uid2uname(uid) );
  29:	}
  30:	
  31:	char *uid2uname(uid_t uid)
  32:	{
  33:	    struct passwd *pwd ;
  34:	        pwd = getpwuid( uid );
  35:	        if( pwd )
  36:	            return( pwd->pw_name );
  37:	        else
  38:	        {
  39:	             static char buf[100] ; /* must be static, bad for multithreading */
  40:	             sprintf(buf,"%d",uid );
  41:	             return( buf );
  42:	        }
  43:	}
  44:	
  45:	char *gid2gname(gid_t gid)
  46:	{
  47:	    struct group *grp ;
  48:	        grp = getgrgid( gid );
  49:	        if( grp )
  50:	            return( grp->gr_name );
  51:	        else
  52:	        {
  53:	             static char buf[100] ; /* must be static, bad for multithreading */
  54:	             sprintf(buf,"%d",gid );
  55:	             return( buf );
  56:	        }
  57:	}
----------------------------------------------------------------------
---------------------------------------------------------------------- % ./proc-uid-print1231: yas 0: root %
----------------------------------------------------------------------
----------------------------------------------------------------------
   1:	/*
   2:	        ystat.c -- stat システム・コールのシェル・インタフェース
   3:	        ~yas/syspro1/file/ystat.c
   4:	        $Header: /home/lab2/OS/yas/syspro1/file/RCS/ystat.c,v 1.4 1998/05/18 13:20:36 yas Exp $
   5:	        Start: 1995/03/07 20:59:12
   6:	*/
   7:	
   8:	#include <sys/types.h>          /* stat(2) */
   9:	#include <sys/stat.h>           /* stat(2) */
  10:	#include <sys/sysmacros.h>      /* major(), minor() */
  11:	#include <stdio.h>
  12:	
  13:	#if     0
  14:	struct  stat {
  15:	        dev_t   st_dev;
  16:	        long    st_pad1[3];     /* reserved for network id */
  17:	        ino_t   st_ino;
  18:	        mode_t  st_mode;
  19:	        nlink_t st_nlink;
  20:	        uid_t   st_uid;
  21:	        gid_t   st_gid;
  22:	        dev_t   st_rdev;
  23:	        long    st_pad2[2];     /* dev and off_t expansion */
  24:	        off_t   st_size;
  25:	        long    st_pad3;        /* future off_t expansion */
  26:	        timestruc_t st_atim;    
  27:	        timestruc_t st_mtim;    
  28:	        timestruc_t st_ctim;    
  29:	        long    st_blksize;
  30:	        blkcnt_t st_blocks;
  31:	        char    st_fstype[_ST_FSTYPSZ];
  32:	        long    st_pad4[8];     /* expansion area */
  33:	};
  34:	#endif
  35:	
  36:	extern  void stat_print( char *path );
  37:	
  38:	main( int argc, char *argv[] )
  39:	{
  40:	        if( argc != 2 )
  41:	        {
  42:	            fprintf( stderr,"Usage:%% %s filename \n",argv[0] );
  43:	            exit( 1 );
  44:	        }
  45:	        stat_print( argv[1] );  /* 引数はファイル */
  46:	}
  47:	
  48:	void stat_print( char *path )
  49:	{
  50:	    struct stat buf ;
  51:	        if( stat( path,&buf ) == -1 )
  52:	        {
  53:	            perror( path );
  54:	            exit( 1 );
  55:	        }
  56:	
  57:	        printf("path: %s\n",path );
  58:	        printf("dev: %d,%d\n",major(buf.st_dev),minor(buf.st_dev) );
  59:	        printf("ino: %d\n",buf.st_ino );
  60:	        printf("mode: 0%o\n",buf.st_mode );
  61:	        printf("nlink: %d\n",buf.st_nlink );
  62:	        printf("uid: %d\n",buf.st_uid );
  63:	        printf("gid: %d\n",buf.st_gid );
  64:	        printf("rdev: %d,%d\n",major(buf.st_rdev),minor(buf.st_rdev) );
  65:	        printf("size: %d\n",buf.st_size );
  66:	        printf("atime: %s",ctime(&buf.st_atime) );
  67:	        printf("mtime: %s",ctime(&buf.st_mtime) );
  68:	        printf("ctime: %s",ctime(&buf.st_ctime) );
  69:	        printf("blksize: %d\n",buf.st_blksize );
  70:	        printf("blocks: %d\n",buf.st_blocks );
  71:	}
----------------------------------------------------------------------
この実行結果から次のようなことがわかる。---------------------------------------------------------------------- % ./ystat ystat.cpath: ystat.c dev: 8,3 ino: 17639075 mode: 0100644 nlink: 1 uid: 1231 gid: 40 rdev: 0,0 size: 1767 atime: Mon May 18 22:20:44 1998 mtime: Mon May 18 22:20:44 1998 ctime: Mon May 18 22:20:44 1998 blksize: 8192 blocks: 4 % /sbin/stat ystat.c
ystat.c: inode 17639075; dev 2097155; links 1; size 1767 regular; mode is rw-r--r--; uid 1231 (yas); gid 40 (lab) st_fstype: nfs3 change time - Mon May 18 22:20:44 1998 <895497644> access time - Mon May 18 22:20:44 1998 <895497644> modify time - Mon May 18 22:20:44 1998 <895497644> %
----------------------------------------------------------------------
 
0100644の上位4ビット、つまり、0170000と AND (C言語のでは、
&演算子)をとった結果は 0100000 となる。この値は、普通のファ
イル(regular file)を意味する。ディレクトリの場合、0040000 となる。こ
れらの数は、
----------------------------------------------------------------------
#define S_IFMT          0xF000  /* type of file */
#define S_IFIFO         0x1000  /* fifo */
#define S_IFCHR         0x2000  /* character special */
#define S_IFDIR         0x4000  /* directory */
#define S_IFBLK         0x6000  /* block special */
#define S_IFREG         0x8000  /* regular */
#define S_IFLNK         0xA000  /* symbolic link */
#define S_IFSOCK        0xC000  /* socket */
#define S_ISFIFO(mode)  ((mode&S_IFMT) == S_IFIFO)
#define S_ISCHR(mode)   ((mode&S_IFMT) == S_IFCHR)
#define S_ISDIR(mode)   ((mode&S_IFMT) == S_IFDIR)
#define S_ISBLK(mode)   ((mode&S_IFMT) == S_IFBLK)
#define S_ISREG(mode)   ((mode&S_IFMT) == S_IFREG)
#define S_ISLNK(m)      (((m) & S_IFMT) == S_IFLNK)
#define S_ISSOCK(m)     (((m) & S_IFMT) == S_IFSOCK)
----------------------------------------------------------------------
プログラム中では、次のようにしてファイルの型を調べることができる。
----------------------------------------------------------------------
	switch( buf.st_mode & S_IFMT )
	{
	case S_IFREG: 
	    ファイルの時の処理;
	    break;
	case S_IFDIR: ...
	    ディレクトリの時の処理;
	    break;
	....
	}
----------------------------------------------------------------------
あるいは、
----------------------------------------------------------------------
	if( S_ISREG(buf.st_mode) )
	{
	    ファイルの時の処理;
	}
	else if( S_ISDIR(buf.st_mode) )
	{
	    ディレクトリの時の処理;
	}
----------------------------------------------------------------------
モードの下位9ビット(上の例では、8進数で 644 )は、許可されたアクセス 方法を表している。その9ビットは、3ビットづつに区切られおり、上位から 所有者(owner)、グループ(group)、その他(others)に許可(permission) されているアクセス方式を表している。所有者(owner)の代りに、利用者 (user)という言葉が使われることもある。
各3ビットは次の様なアクセス方法が許可されていることを意味する。
---------------------------------------------------------------------- ls -l 3ビット値 アクセス権 ---------------------------------------------------------------------- r 4 読込み可能 w 2 書込み可能 x 1 実行可能(ディレクトリの場合は、検索可能) ----------------------------------------------------------------------このように、普通のファイルとディレクトリで "x" の意味が異なる。
umask コマンドコマンドを使う。
YHM_umask コマンドは、シェルの 内部コマンドである。マスクは、プロセスの属性の1つで、 子プロセス、孫プロセスと代々受け 継がれていきく。現在のシェルのマスクを見れば、 シェルから実行されるプロセスのマスクがわかる。---------------------------------------------------------------------- % umask22 %
----------------------------------------------------------------------
umask を操作するシステム・コールは、umask(コマンドと同じ名前)である。
マスクは、モードと同じく8進数で考える。たとえば、022 は、次のように 読む。
----------------------------------------------------------------------
   ---       000 新しいファイルは、自分自身は、読み書き自由。
      -w-    020 同じグループの人は、ファイルの書込みを禁止する。
+)       -w- 002 その他の人も、ファイルの書込みを禁止する。
----------------
             022
----------------------------------------------------------------------
このように、モードの逆になる。
マスクを変えるには、umask コマンドに、引数を与えて実行します。
この例では、シェルのマスクを 022 から 066 へ変更しています。---------------------------------------------------------------------- % umask22 % % umask 066
% umask
66 %
----------------------------------------------------------------------
umask コマンドによるマスクの設定は、
普通、~/.cshrc や ~/.login に入れる。
rsh コマンド時も実行さ
れるように、~/.cshrc に入れて置くのが無難である。
rw-rw-rw-)になることが多い。これは、ファイルを作るプロ
グラム(cp, mule などで)で、次のようにファイルを作っていることに由来す
る。
	open("file1",O_CREAT|O_TRUNC,0666);
すると、UNIXオペレーティング・システムのカーネルは、システム・コールの
引数とマスクを保持している変数 mask を使って
mode = 0666 & ~mask ;というモードのファイルを作る。「
&」
は、ビットごとのAND、「~」は、ビット反
転である。この場合、繰り下がりがないので、
mode = 0666 - mask ;と考えてもよい。結果として、マスクの部分のビットが落ちたモード を持つファイルが作られる。
たとえば、マスクが 022 の時、作
成されるファイルのモードは、666からマスクの022を引くので、次のように 
644 (rw-r--r--) になる。
マスクを 0 にして、同じことをすると、モードが 666 になる。---------------------------------------------------------------------- % umask22 % echo "This is file1" > file1
% ls -l file1
-rw-r--r-- 1 yas 14 Sep 14 17:25 file1 %
----------------------------------------------------------------------
---------------------------------------------------------------------- % umask 000% umask
0 % rm file1
% echo "This is file1" > file1
% ls -l file1
-rw-rw-rw- 1 yas 14 Sep 14 17:29 file1 %
----------------------------------------------------------------------
マスクは、新しくファイルを作る時にのみ有効である。すでに、ファイルが存 在する場合、ファイルに書込みをしても、モードは変わらない。たとえば、
新しくディレクトリを作る時、そのモードは、最大 777 (---------------------------------------------------------------------- % chmod 777 file1% ls -l file1
-rwxrwxrwx 1 yas 14 Jan 23 16:28 file1 % umask 022
% echo "a" > file1
% ls -l file1
-rwxrwxrwx 1 yas 2 Jan 23 18:05 file1 %
----------------------------------------------------------------------
rwxrwxrwx)になることが多い。これは、ディレクトリを作る
プログラムが、
	mkdir("dir1",0777 );
となっているからである。この結果、ファイルと同様に
mode = 0777 & ~maskというモードを持つディレクトリが作られる。
たとえば、マスクが 022 の時、作成されるディレクトリのモードは、777から
マスクの022を引くので、次のように 755 (rwxr-xr-x) に
なる。
マスクを 0 にして、同じことをすると、---------------------------------------------------------------------- % umask22 % mkdir dir1
% ls -ld dir1
drwxr-xr-x 2 yas 512 Jan 23 18:09 dir1 %
----------------------------------------------------------------------
新しく YHM_mkdir() コマンドで作ったディレクトリの モードが 777 になる。---------------------------------------------------------------------- % umask 0% rmdir dir1
% mkdir dir1
% ls -ld dir1
drwxrwxrwx 2 yas 512 Jan 23 18:09 dir1 %
----------------------------------------------------------------------
UNIXは、マルチユーザのシステムなので、ユーザ1人ひとりを認識するような アクセス制御の仕組みを持っている。たとえば、UNIX では、共同プロジェク トに関連したファイルは、他のユーザにも見せてもよいが、個人の電子メール は、他の人には見せないといった制御ができる。
これに対して、パーソナル・コンピュータ用のオペレーティング・システムで は、このような複数のユーザを識別したアクセス制御はできない。
 
ファイルの「内容」のアクセス3段階であるが、ファイルの「属性」次の2段 階である。
----------------------------------------------------------------------
% ls -li /usr/bsd/{compress,uncompress,zcat} ![[←]](../icons/screen-return.gif) 1048720 -rwxr-xr-x    1 root     sys        33700   3月 28日 10時57分 /usr/bsd/compress
1048618 lrwxr-xr-x    1 root     sys            8   2月  4日 05時50分 /usr/bsd/uncompress -> compress
1048620 lrwxr-xr-x    1 root     sys            8   2月  4日 05時50分 /usr/bsd/zcat -> compress
%
1048720 -rwxr-xr-x    1 root     sys        33700   3月 28日 10時57分 /usr/bsd/compress
1048618 lrwxr-xr-x    1 root     sys            8   2月  4日 05時50分 /usr/bsd/uncompress -> compress
1048620 lrwxr-xr-x    1 root     sys            8   2月  4日 05時50分 /usr/bsd/zcat -> compress
% ![[]](../icons/screen-cursor.gif) ----------------------------------------------------------------------
----------------------------------------------------------------------
ディレクトリを stat(2) で調べると、リンク数が2以上になっている。
"." と 子ディレクトリの ".." の分だけ増えている。
本名は、open(), creat(), mkdir() の時に作られる。それ以外に、link() シ ステム・コールで増やすことができる。リンクを減らすには、unlink() シス テム・コールを使う。リンクが1つしかないファイルに unlink() を行うと、 ファイルが削除される。
シンボリック・リンクは、別のファイルの名前(シンボル)を含んでいる特殊 なファイルである。シンボリック・リンクは、symlink() システム・コールで 作成することができる。
ls -l では、3つの時刻のうち、どの時刻が表示されているのかを調べなさい。 また、他の2つの時刻を表示させる方法を調べなさい。
myls-l の結果の表示形式は、ls -l と完全に一致しなくてもよい。たとえば、 時刻の表示は、上の ystat.c の結果と同じでもよい。ocaltime(), strftime() ライブラリ関数を利用すると、時刻の表示をより簡単に ls -l の 表示に近づけることができるであろう。
uid (st_uid) については、ls -l では、ユーザ名(ログイン名)で表示され る。myls-l では、数字のままでよい。ライブラリ関数 getpwuid() を使えば、 UID からユーザ名を調べることができる。 ~yas/syspro1/proc/proc-uid-print.c にあるuid2uname(), gid2gname() を参 考にするとよい。
プログラムの引数となるファイルの数は、1個とする。複数のファイルについ て、ls -l と同様の表示をするように拡張してもよい。
引数としてディレクトリの名前が与えられた場合にも、ディレクトリの内容で はなくディレクトリ自身の属性を表示する。シンボリック・リンクには対応し なくてもよい。(よって正確には、ls -l filename ではなく、ls -ldL filename である。)
余裕があれば、lstat() と readlink() の、2つのシステム・コールを用いて、 ls -l と同じようにシンボリック・リンクの内容を表示しなさい。
さらに、opendir(3) や scandir(3) を使って、ディレクトリの内容を表示 するようにしてもよい。
% access_check filename user readでは、ユーザ名(または、UID)が user のユーザがfilename で与えられたファ イルを読むことができるかどうかを調べる。user は、番号(UID)かユーザ名で 与える。アクセス方法には、次のようなものがある。
% present user 1998/05/19-10:10 1998/05/19-11:30と与えると、ユーザ名user のユーザが、その時間にログインしていたら、OK を返す。
Subject: [syspro1/report3] last
Subject: [syspro1/report3] ls-dir
Subject: [syspro1/report3] ls-l
Subject: [syspro1/report3] touch
Subject: [syspro1/report3] tail
Subject: [syspro1/report3] access
Subject: [syspro1/report3] present