Go to the first, previous, next, last section, table of contents.


サンプル集

SIGCHLDの受信

#include <sys/types.h>  /* ほかのsysヘッダより先にこのファイルをinlcudeする */
#include <sys/wait.h>   /* waitpid()とマクロのヘッダ */
#include <signal.h>     /* シグナル関数のヘッダ */
#include <stdio.h>      /* fprintf()のヘッダ */
#include <unistd.h>     /* fork()のヘッダ */

void sig_chld(int);     /* SIGCHLDハンドラのプロトタイプ宣言 */

int main() 
{
    struct sigaction act;
    pid_t pid;

    /* sig_chldをSIGCHLDハンドラとする */
    act.sa_handler = sig_chld;

    /* この例では、そのほかのシグナルはブロックしない */
    sigemptyset(&act.sa_mask);

    /*
     * 関心があるのは終了した子プロセスだけで、(例えばユーザが端末で
     * control-Z を押して)停止したプロセスには関心がない
     */
    act.sa_flags = SA_NOCLDSTOP;

    /*
     * 以上の値を使って設定する。実際のアプリケーションを書くときには
     * NULLを渡さずに古い値を保存するだろう
     */
    if (sigaction(SIGCHLD, &act, NULL) < 0) 
    {
        fprintf(stderr, "sigaction failed\n");
        return 1;
    }

    /* Fork */
    switch (pid = fork())
    {
    case -1:
        fprintf(stderr, "fork failed\n");
        return 1;

    case 0:                         /* 子プロセス -- 直ちに終了 */
        _exit(7);                   /* 終了ステータス = 7 */

    default:                        /* 親プロセス */
        sleep(10);                  /* 子プロセスの終了を待つ */
    }

    return 0;
}

/*
 * シグナルハンドラ関数 -- SIGCHLDを受信したとき、つまり子プロセスが終了
 * したときだけ呼ばれる
 */
void sig_chld(int signo) 
{
    int status, child_val;

    /* ブロックせずに子プロセスの終了を待つ */
    if (waitpid(-1, &status, WNOHANG) < 0) 
    {
        /*
         * シグナルハンドラでfprintf()のような標準入出力関数を
         * 呼ぶべきではないのだが、このようなちょっとだけのプ
         * ログラムでならまあよかろう
         */
        fprintf(stderr, "waitpid failed\n");
        return;
    }

    /*
     * statusに情報が入っているので、wait.hのマクロを使って取り出す
     */
    if (WIFEXITED(status))                /* 子プロセスは正常終了したか? */
    {
        child_val = WEXITSTATUS(status); /* 終了ステータスを得る */
        printf("child's exited normally with status %d\n", child_val);
    }
}

プロセステーブルの読み込み -- SUNOS 4 バージョン

#define _KMEMUSER
#include <sys/proc.h>
#include <kvm.h>
#include <fcntl.h>
 
char regexpstr[256];
#define INIT            register char *sp=regexpstr;
#define GETC()          (*sp++)
#define PEEKC()         (*sp)
#define UNGETC(c)       (--sp)
#define RETURN(pointer) return(pointer);
#define ERROR(val)
#include <regexp.h>
 
pid_t
getpidbyname(char *name,pid_t skipit)
{
    kvm_t *kd;
    char **arg;
    int error;
    char *p_name=NULL;
    char expbuf[256];
    char **freeme;
    int curpid;
    struct user * cur_user;
    struct user myuser;
    struct proc * cur_proc;
 
 
    if((kd=kvm_open(NULL,NULL,NULL,O_RDONLY,NULL))==NULL){
        return(-1);
    }
    sprintf(regexpstr,"^.*/%s$",name);
    compile(NULL,expbuf,expbuf+256,'\0');
 
    while(cur_proc=kvm_nextproc(kd)){
        curpid = cur_proc->p_pid;
        if((cur_user=kvm_getu(kd,cur_proc))!=NULL){
            error=kvm_getcmd(kd,cur_proc,cur_user,&arg,NULL);
            if(error==-1){
                if(cur_user->u_comm[0]!='\0'){
                    p_name=cur_user->u_comm;
                }
            }
            else{
                p_name=arg[0];
            }
        }
        if(p_name){
            if(!strcmp(p_name,name)){
                if(error!=-1){
                    free(arg);
                }
                if(skipit!=-1 && ourretval==skipit){
                    ourretval=-1;
                }
                else{
                    close(fd);
                    break;
                }
                break;
            }
            else{
                if(step(p_name,expbuf)){
                    if(error!=-1){
                        free(arg);
                    }
                    break;
                }
            }
        }
        if(error!=-1){
            free(arg);
        }
        p_name=NULL;
    }
    kvm_close(kd);
    if(p_name!=NULL){
        return(curpid);
    }
    return (-1);
}

プロセステーブルの読み込み -- SYSV バージョン

pid_t
getpidbyname(char *name,pid_t skipit)
{
    DIR  *dp;
    struct dirent *dirp;
    prpsinfo_t retval;
    int fd;
    pid_t ourretval=-1;
 
    if((dp=opendir("/proc"))==NULL){
        return -1;
    }
    chdir("/proc");
    while((dirp=readdir(dp))!=NULL){
        if(dirp->d_name[0]!='.'){
            if((fd=open(dirp->d_name,O_RDONLY))!=-1){
                if(ioctl(fd,PIOCPSINFO,&retval)!=-1){
                    if(!strcmp(retval.pr_fname,name)){
                        ourretval=(pid_t)atoi(dirp->d_name);
                        if(skipit!=-1 && ourretval==skipit){
                            ourretval=-1;
                        }
                        else{
                            close(fd);
                            break;
                        }
                    }
                }
                close(fd);
            }
        }
    }
    closedir(dp);
    return ourretval;
}

プロセステーブルの読み込み -- AIX 4.2 バージョン

#include <stdio.h>
#include <procinfo.h>

int getprocs(struct procsinfo *, int, struct fdsinfo *,
             int, pid_t *, int);

pid_t getpidbyname(char *name, pid_t *nextPid)
{
  struct procsinfo  pi;
  pid_t             retval = (pid_t) -1;
  pid_t             pid;

  pid = *nextPid;

  while(1)
  {
    if(getprocs(&pi, sizeof pi, 0, 0, &pid, 1) != 1)
      break;
    
    if(!strcmp(name, pi.pi_comm))
    {
      retval = pi.pi_pid;
      *nextPid = pid;
      break;
    }
  }
  
  return retval;
}

int main(int argc, char *argv[])
{
  int   curArg;
  pid_t pid;
  pid_t nextPid;

  if(argc == 1)
  {
    printf("syntax: %s <program> [program ...]\n",argv[0]);
    exit(1);
  }

  for(curArg = 1; curArg < argc; curArg++)
  {
    printf("Process IDs for %s\n", argv[curArg]);

    for(nextPid = 0, pid = 0; pid != -1; )
      if((pid = getpidbyname(argv[curArg], &nextPid)) != -1)
        printf("\t%d\n", pid);
  }
}

popenとpsを用いたプロセステーブルの読み込み

#include <stdio.h>      /* FILE, sprintf, fgets, puts */
#include <stdlib.h>     /* atoi, exit, EXIT_SUCCESS */
#include <string.h>     /* strtok, strcmp */
#include <sys/types.h>  /* pid_t */
#include <sys/wait.h>   /* WIFEXITED, WEXITSTATUS */

char *procname(pid_t pid)
{
   static char line[133], command[80], *linep, *token, *cmd;
   FILE *fp;
   int status;

   if (0 == pid) return (char *)0;

   sprintf(command, "ps -p %d 2>/dev/null", pid);
   fp = popen(command, "r");
   if ((FILE *)0 == fp) return (char *)0;

   /* 先頭行を読む */
   if ((char *)0 == fgets(line, sizeof line, fp))
   {
      pclose(fp);
      return (char *)0;
   }

   /* 見出しの中でコマンド名のカラムを調べる
    * (BSD系のマシンでは5番目のカラムにCOMMANDが来る。
    *  一方SysVでは4番目のカラムにCMDかCOMMANDが来る)。
    */
   for (linep = line; ; linep = (char *)0)
   {
      if ((char *)0 == (token = strtok(linep, " \t\n")))
      {
         pclose(fp);
         return (char *)0;
      }
      if (0 == strcmp("COMMAND", token) || 0 == strcmp("CMD", token))
      { /* COMMANDカラム発見 */
         cmd = token;
         break;
      }
   }

   /* ps(1)の出力の行を読む */
   if ((char *)0 == fgets(line, sizeof line, fp))
   {
      pclose(fp);
      return (char *)0;
   }

   /* コマンド見出しの下に来る単語を取り出す */
   if ((char *)0 == (token = strtok(cmd, " \t\n")))
   {
      pclose(fp);
      return (char *)0;
   }

   status = pclose(fp);
   if (!WIFEXITED(status) || 0 != WEXITSTATUS(status)) 
     return (char *)0;

   return token;
}

int main(int argc, char *argv[])
{
   puts(procname(atoi(argv[1])));
   exit(EXIT_SUCCESS);
}

デーモンユーティリティ関数

#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>

/* closeall() -- 指定した値以上のFDを全て閉じる */

void closeall(int fd)
{
    int fdlimit = sysconf(_SC_OPEN_MAX);

    while (fd < fdlimit)
      close(fd++);
}

/* daemon() - プロセスをユーザから切り放してバックグラウンドに隠れる
 * 失敗すると-1を返すが、既にforkしているかも知れないので終了す
 * る以外にたいしたことはできない。この例はBSDバージョンに基づい
 * ているので、呼び出し側はumaskなどを設定する責任がある。
 */

/* 全てのPosixシステムで動作するはず */

int daemon(int nochdir, int noclose)
{
    switch (fork())
    {
        case 0:  break;
        case -1: return -1;
        default: _exit(0);          /* exit 元のプロセスを終了する */
    }

    if (setsid() < 0)               /* 失敗しないはず */
      return -1;
 
    /* 後で制御ttyを得るのであればこのswitch文を外す */
    /* -- デーモンでは通常は望ましくない */

    switch (fork())
    {
        case 0:  break;
        case -1: return -1;
        default: _exit(0);
    }

    if (!nochdir)
      chdir("/");

    if (!noclose)
    {
        closeall(0);
        open("/dev/null",O_RDWR);
        dup(0); dup(0);
    }

    return 0;
}

/* fork2() -- forkと同じだが新しいプロセスは直ちに親プロセスを失う
 *            (終了時にゾンビにならない)
 * 親プロセスには有効なpidではなく1を返す。
 * 親プロセスは新しいプロセス(親子関係ではない)をwait()出来ない。
 */
     
/* このバージョンではSIGCHLDを受信したり無視したり*していない*ことが */
/* 前提である。そうしていた場合は代わりにfork()を使う。 */ 

int fork2()
{
    pid_t pid;
    int rc;
    int status;

    if (!(pid = fork()))
    {
        switch (fork())
        {
          case 0:  return 0;
          case -1: _exit(errno);    /* errnoはどれも256未満と仮定 */
          default: _exit(0);
        }
    }

    if (pid < 0 || waitpid(pid,&status,0) < 0)
      return -1;

    if (WIFEXITED(status))
      if (WEXITSTATUS(status) == 0)
        return 1;
      else
        errno = WEXITSTATUS(status);
    else
      errno = EINTR;  /* 何らかの :-) 割り込みがあった */

    return -1;
}

上の関数の使用例:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
#include <errno.h>

int daemon(int,int);
int fork2(void);
void closeall(int);

#define TCP_PORT 8888

void errexit(const char *str)
{
    syslog(LOG_INFO, "%s failed: %d (%m)", str, errno);
    exit(1);
}

void errreport(const char *str)
{
    syslog(LOG_INFO, "%s failed: %d (%m)", str, errno);
}

/* 実際の子プロセスがこれ */

void run_child(int sock)
{
    FILE *in = fdopen(sock,"r");
    FILE *out = fdopen(sock,"w");
    int ch;

    setvbuf(in, NULL, _IOFBF, 1024);
    setvbuf(out, NULL, _IOLBF, 1024);

    while ((ch = fgetc(in)) != EOF)
      fputc(toupper(ch), out);

    fclose(out);
}

/* これがデーモンの主たる仕事 -- 接続を監視してspawnする */     

void process()
{
    struct sockaddr_in addr;
    int addrlen = sizeof(addr);
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    int flag = 1;
    int rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
                        &flag, sizeof(flag));

    if (rc < 0)
      errexit("setsockopt");

    addr.sin_family = AF_INET;
    addr.sin_port = htons(TCP_PORT);
    addr.sin_addr.s_addr = INADDR_ANY;

    rc = bind(sock, (struct sockaddr *) &addr, addrlen);
    if (rc < 0)
      errexit("bind");

    rc = listen(sock, 5);
    if (rc < 0)
      errexit("listen");

    for (;;)
    {
        rc = accept(sock, (struct sockaddr *) &addr, &addrlen);

        if (rc >= 0)
          switch (fork2())
          {
            case 0:  close(sock); run_child(rc); _exit(0);
            case -1: errreport("fork2"); close(rc); break;
            default: close(rc);
          }
    }
}

int main()
{
    if (daemon(0,0) < 0)
    {
        perror("daemon");
        exit(2);
    }

    openlog("test", LOG_PID, LOG_DAEMON);

    process();

    return 0;
}

モデム制御の例

/* 簡単なモデムコマンドを発行する
 * シリアルデバイス(ダイアルアウトデバイスか非モデム制御デバイスがよい)
 * の名前が引数に必要。
 * 機能するダイアルアウトデバイスがないなら、CLOCALの代りにCFLAGS_TO_SETに
 * すること。
 */

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>   /* システムによっては必要 */
#include <termios.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>

#define CFLAGS_TO_SET (CREAD | HUPCL)
#define CFLAGS_TO_CLEAR (CSTOPB | PARENB | CLOCAL)

enum flowmode { NoFlow, HardFlow, SoftFlow };

/* システム依存 */
#define CFLAGS_HARDFLOW (CRTSCTS)

#define EXAMPLE_BAUD B19200
#define EXAMPLE_FLOW HardFlow

static void die(const char *msg)
{
    fprintf(stderr, "%s\n", msg);
    exit(1);
}

static int close_and_complain(int fd, const char *msg, int err)
{
    fprintf(stderr, "%s: %s\n", msg, strerror(err));
    if (fd >= 0)
        close(fd);
    errno = err;
    return -1;
}

int open_port(const char *name, speed_t baud, enum flowmode flow)
{
    int flags;
    struct termios attr;

    int fd = open(name, O_RDWR | O_NONBLOCK | O_NOCTTY);

    if (fd < 0)
        return close_and_complain(-1, "open", errno);

    /* だいたいの設定を得る */

    if (tcgetattr(fd, &attr) < 0)
        return close_and_complain(fd, "tcgetattr", errno);
        
    /* 特別な入出力処理はなし */

    attr.c_iflag = (flow == SoftFlow) ? (IXON | IXOFF) : 0;
    attr.c_oflag = 0;

    /* 8ビット文字サイズと雑多な制御モードの設定 */

    attr.c_cflag &= ~(CSIZE | CFLAGS_TO_CLEAR | CFLAGS_HARDFLOW);
    attr.c_cflag |= (CS8 | CFLAGS_TO_SET);
    if (flow == HardFlow)
        attr.c_cflag |= CFLAGS_HARDFLOW;
    
    /* ローカルモード */

    attr.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ISIG);

    /* 特殊文字 -- 最初の設定でほとんどは無効になる */

    {
        int i;
#ifdef _POSIX_VDISABLE
        attr.c_cc[0] = _POSIX_VDISABLE;
#else
        attr.c_cc[0] = fpathconf(fd, _PC_VDISABLE);
#endif
        for (i = 1; i < NCCS; i++)
            attr.c_cc[i] = attr.c_cc[0];
    }

    attr.c_cc[VSTART] = 0x11;
    attr.c_cc[VSTOP] = 0x13;

    /* read()のタイミング制御 */

    attr.c_cc[VMIN] = 1;
    attr.c_cc[VTIME] = 0;

    /* ボーレート */

    cfsetispeed(&attr, baud);
    cfsetospeed(&attr, baud);

    /* 設定を書き込む */

    if (tcsetattr(fd, TCSANOW, &attr) < 0)
        return close_and_complain(fd, "tcsetattr", errno);

    /* デバイスにO_NONBLOCKの設定が残っていれば解除する */

    flags = fcntl(fd, F_GETFL, 0);
    if (flags < 0)
        return close_and_complain(fd, "fcntl(GETFL)", errno);
    if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) < 0)
        return close_and_complain(fd, "fcntl(SETFL)", errno);

    return fd;
}

/* 簡単なタイミングユーティリティを幾つか */

/* *tvにsecsとusecsを加える */

static void timeradd(struct timeval *tv, long secs, long usecs)
{
    tv->tv_sec += secs;
    if ((tv->tv_usec += usecs) >= 1000000)
    {
        tv->tv_sec += tv->tv_usec / 1000000;
        tv->tv_usec %= 1000000;
    }
}

/* *res = *a - *b を設定し、結果の符号を返す */

static int timersub(struct timeval *res,
                    const struct timeval *a, const struct timeval *b)
{
    long sec = a->tv_sec - b->tv_sec;
    long usec = a->tv_usec - b->tv_usec;

    if (usec < 0)
        usec += 1000000, --sec;

    res->tv_sec = sec;
    res->tv_usec = usec;

    return (sec < 0) ? (-1) : ((sec == 0 && usec == 0) ? 0 : 1);
}

/* おかしな文字列(例えば ababc)はチェックも扱いもしない。
 * タイムアウトの単位はミリ秒。
 * より一般的な方法はタイムアウトにalarm()を使うことだが、
 * この例では簡単のためシグナル処理を避け別の方法を示している。
 */

int expect(int fd, const char *str, int timeo)
{
    int matchlen = 0;
    int len = strlen(str);
    struct timeval now,end,left;
    fd_set fds;
    char c;

    gettimeofday(&end, NULL);
    timeradd(&end, timeo/1000, timeo%1000);

    while (matchlen < len)
    {
        gettimeofday(&now, NULL);
        if (timersub(&left, &end, &now) <= 0)
            return -1;

        FD_ZERO(&fds);
        FD_SET(fd, &fds);
        if (select(fd+1, &fds, NULL, NULL, &left) <= 0)
            return -1;

        if (read(fd, &c, 1) != 1)
            return -1;

        if (isprint((unsigned char)c) || c == '\n' || c == '\r')
            putchar(c);
        else
            printf("\\x%02x", c);

        if (c == str[matchlen])
            ++matchlen;
        else
            matchlen = 0;
    }

    return 0;
}

int main(int argc, char **argv)
{
    int fd;
    unsigned char c;

    if (argc < 2)
        die("no port specified");

    setvbuf(stdout, NULL, _IONBF, 0);

    fd = open_port(argv[1], EXAMPLE_BAUD, EXAMPLE_FLOW);
    if (fd < 0)
        die("cannot open port");

    write(fd, "AT\r", 3);
    if (expect(fd, "OK", 5000) < 0)
    {
        write(fd, "AT\r", 3);
        if (expect(fd, "OK", 5000) < 0)
        {
            tcflush(fd, TCIOFLUSH);
            close(fd);
            die("no response to AT");
        }
    }

    write(fd, "ATI4\r", 5);
    expect(fd, "OK", 10000);

    putchar('\n');

    tcflush(fd, TCIOFLUSH);
    close(fd);

    return 0;
}

ジョブ制御の例


/* フォアグラウンド/バックグラウンドジョブを起動する関数群 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>

/* 以下の関数の幾つかは、制御端末を特定できなかったり呼び出しプロセ
 * スがフォアグラウンドでなかったら失敗する。前者の場合、フォアグラ
 * ウンドプロセスがstdin、stdout、stderrのいずれかで制御端末をオー
 * プンしているものと仮定し、そうでなければENOTTYを返す。
 * 後者の場合、フォアグラウンドでないプロセスが(おそらく過度に神経
 * 質に)何かをフォアグラウンドにしようとすると、foreground_self()
 * という特別な場合以外はEPERMを返す。

/* 端末(cttyとしてオープンされている)を指定したpgrpに割り当てる。
 * この tcsetpgrp()のラッパーが必要なのは、ひとえにPOSIXの該当部分が
 * 酷いいんちきだからである。
 * このいんちきに従うシステムはtcsetpgrpがフォアグラウンドでないプロセス
 * から呼ばれるとSIGTTOUを送る(ほとんどいつも)。
 * 常識に反した見せかけだけの一貫性がまかり通ってしまっている。
 */

int assign_terminal(int ctty, pid_t pgrp)
{
    sigset_t sigs;
    sigset_t oldsigs;
    int rc;

    sigemptyset(&sigs);
    sigaddset(&sigs,SIGTTOU);
    sigprocmask(SIG_BLOCK, &sigs, &oldsigs);

    rc = tcsetpgrp(ctty, pgrp);

    sigprocmask(SIG_SETMASK, &oldsigs, NULL);

    return rc;
}

/* fork()に似ているが、ジョブ制御を行う。fgが真ならば新しく作成さ
 * れたプロセスはフォアグラウンドにされる (呼び出したプロセスはバッ
 * クグラウンドになるということであるから、以後のtty入出力には注意)。
 * 新しいジョブを作成する場合はpgrpを-1にする。このとき、返される
 * pidは新しいジョブのプロセスグループIDでもある。新しいジョブを作
 * 成しない場合はpgrpに同じセッション内のジョブを指定する(通常はパ
 * イプラインの2番目以降のプロセスを開始するためにだけ使う)。
 */

pid_t spawn_job(int fg, pid_t pgrp)
{
    int ctty = -1;
    pid_t pid;

    /* *新しく*フォアグラウンドのジョブを立ち上げるなら、stdin、
     * stdout、stderrの少なくとも一つは制御端末を指していて、カレン
     * トプロセスがフォアグラウンドであることが必要。
     * 既存のジョブの中で新しくフォアグラウンドプロセスを開始するの
     * であれば制御端末のみチェックする。
     * 制御端末のないセッションではバックグラウンドジョブのみ可能。
     */
    
    if (fg)
    {
        pid_t curpgrp;

        if ((curpgrp = tcgetpgrp(ctty = 2)) < 0
            && (curpgrp = tcgetpgrp(ctty = 0)) < 0
            && (curpgrp = tcgetpgrp(ctty = 1)) < 0)
            return errno = ENOTTY, (pid_t)-1;

        if (pgrp < 0 && curpgrp != getpgrp())
            return errno = EPERM, (pid_t)-1;
    }

    switch (pid = fork())
    {
        case -1: /* fork失敗 */
            return pid;

        case 0: /* 子プロセス */
            
            /* 新しいプロセスグループをつくり、必要であれば自分たち
	     * 自身をフォアグラウンドにする。
             * setpgidが失敗した場合(起こり得ないケース)どうするか
             * は定かではない。
             */

            if (pgrp < 0)
                pgrp = getpid();

            if (setpgid(0,pgrp) == 0 && fg)
                assign_terminal(ctty, pgrp);

            return 0;

        default: /* 親 */

            /* ここでも子プロセスグループを作成 */

            if (pgrp < 0)
                pgrp = pid;

            setpgid(pid, pgrp);

            return pid;
    }

    /*NOTREACHED*/
}

/* シグナルsignoをジョブpgrpに送る */

int kill_job(pid_t pgrp, int signo)
{
    return kill(-pgrp, signo);
}

/* ジョブpgrpをサスペンドする */

int suspend_job(pid_t pgrp)
{
    return kill_job(pgrp, SIGSTOP);
}

/* ジョブpgrpをバックグラウンドで再開する */

int resume_job_bg(pid_t pgrp)
{
    return kill_job(pgrp, SIGCONT);
}

/* ジョブpgrpをフォアグラウンドで再開する */

int resume_job_fg(pid_t pgrp)
{
    pid_t curpgrp;
    int ctty;

    if ((curpgrp = tcgetpgrp(ctty = 2)) < 0
        && (curpgrp = tcgetpgrp(ctty = 0)) < 0
        && (curpgrp = tcgetpgrp(ctty = 1)) < 0)
        return errno = ENOTTY, (pid_t)-1;

    if (curpgrp != getpgrp())
        return errno = EPERM, (pid_t)-1;

    if (assign_terminal(ctty, pgrp) < 0)
        return -1;

    return kill_job(pgrp, SIGCONT);
}
    

/* 自分たち自身をフォアグラウンドにする。例えばフォアグラウンドジョ
 * ブをサスペンドした場合など。
 */

int foreground_self()
{
    pid_t curpgrp;
    int ctty;

    if ((curpgrp = tcgetpgrp(ctty = 2)) < 0
        && (curpgrp = tcgetpgrp(ctty = 0)) < 0
        && (curpgrp = tcgetpgrp(ctty = 1)) < 0)
        return errno = ENOTTY, (pid_t)-1;

    return assign_terminal(ctty, getpgrp());
}

/* closeall() - ある値以上のファイルディスクリプタを全て閉じる */

void closeall(int fd)
{
    int fdlimit = sysconf(_SC_OPEN_MAX);

    while (fd < fdlimit)
        close(fd++);
}

/* system()に似ているが、コマンドをバックグラウンドジョブとして実行
 * し、シェルプロセスのプロセスidを返す (これはジョブのプロセスグルー
 * プidでもあり、kill_jobなどで使える)。
 * info、outfd、errfdがNULLでなければ、パイプをオープンし、それぞれ
 * のパイプの親プロセス側のディスクリプタを格納する。
 * これらのうちNULLのものは、子プロセスで/dev/nullにリダイレクトさ
 * れる。
 * また、子プロセスでは2より大きい全てのディスクリプタを閉じる(よく
 * 見過ごされる処理である)。
 */

pid_t spawn_background_command(const char *cmd,
                               int *infd, int *outfd, int *errfd)
{
    int nullfd = -1;
    int pipefds[3][2];
    int error = 0;

    if (!cmd)
        return errno = EINVAL, -1;

    pipefds[0][0] = pipefds[0][1] = -1;
    pipefds[1][0] = pipefds[1][1] = -1;
    pipefds[2][0] = pipefds[2][1] = -1;

    if (infd && pipe(pipefds[0]) < 0)
        error = errno;
    else if (outfd && pipe(pipefds[1]) < 0)
        error = errno;
    else if (errfd && pipe(pipefds[2]) < 0)
        error = errno;

    if (!error && !(infd && outfd && errfd))
    {
        nullfd = open("/dev/null",O_RDWR);
        if (nullfd < 0)
            error = errno;
    }

    if (!error)
    {
        pid_t pid = spawn_job(0, -1);
        switch (pid)
        {
            case -1: /* fork失敗 */
                error = errno;
                break;
                
            case 0: /* 子プロセス */
                
                dup2(infd ? pipefds[0][0] : nullfd, 0);
                dup2(outfd ? pipefds[1][1] : nullfd, 1);
                dup2(errfd ? pipefds[2][1] : nullfd, 2);
                closeall(3);
                
                execl("/bin/sh","sh","-c",cmd,(char*)NULL);

                _exit(127);

            default: /* 親プロセス */

                close(nullfd);
                if (infd)
                    close(pipefds[0][0]), *infd = pipefds[0][1];
                if (outfd)
                    close(pipefds[1][1]), *outfd = pipefds[1][0];
                if (errfd)
                    close(pipefds[2][1]), *errfd = pipefds[2][0];

                return pid;
        }
    }

    /* エラーの場合のみ実行される */

    {
        int i,j;
        for (i = 0; i < 3; ++i)
            for (j = 0; j < 2; ++j)
                if (pipefds[i][j] >= 0)
                    close(pipefds[i][j]);
    }
    
    if (nullfd >= 0)
        close(nullfd);

    return errno = error, (pid_t) -1;
}

/*--------------------------------------------------------------------*/
/* 上記の関数のいささかトリビアルな利用例                             */

pid_t bgjob = -1;
volatile int signo = 0;

#ifndef WCOREDUMP
 /* WCOREDUMPが定義されていなければ、適切な定義を行う(これは普通は
  * (status & 0x80)だが必ずではない)か、コアダンプしないものと
  * 決めてうっちゃる(この例のように)。
  */
# define WCOREDUMP(status) (0)
#endif

int check_children()
{
    pid_t pid;
    int status;
    int count = 0;

    while ((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0)
    {
        if (pid == bgjob && !WIFSTOPPED(status))
            bgjob = -1;

        ++count;

        if (WIFEXITED(status))
            fprintf(stderr,"Process %ld exited with return code %d\n",
                    (long)pid, WEXITSTATUS(status));
        else if (WIFSIGNALED(status))
            fprintf(stderr,"Process %ld killed by signal %d%s\n",
                    (long)pid, WTERMSIG(status),
                    WCOREDUMP(status) ? " (core dumped)" : "");
        else if (WIFSTOPPED(status))
            fprintf(stderr,"Process %ld stopped by signal %d\n",
                    (long)pid, WSTOPSIG(status));
        else
            fprintf(stderr,"Unexpected status - pid=%ld, status=0x%x\n",
                    (long)pid, status);
    }

    return count;
}

void sighandler(int sig)
{
    if (sig != SIGCHLD)
        signo = sig;
}

int main()
{
    struct sigaction act;
    int sigcount = 0;

    act.sa_handler = sighandler;
    act.sa_flags = 0;
    sigemptyset(&act.sa_mask);
    sigaction(SIGINT,&act,NULL);
    sigaction(SIGQUIT,&act,NULL);
    sigaction(SIGTERM,&act,NULL);
    sigaction(SIGTSTP,&act,NULL);
    sigaction(SIGCHLD,&act,NULL);

    fprintf(stderr,"Starting background job 'sleep 60'\n");
    bgjob = spawn_background_command("sleep 60", NULL, NULL, NULL);
    if (bgjob < 0)
    {
        perror("spawn_background_command");
        exit(1);
    }
    fprintf(stderr,"Background job started with id %ld\n", (long)bgjob);
    while (bgjob >= 0)
    {
        if (signo)
        {
            fprintf(stderr,"Signal %d caught\n", signo);
            if (sigcount++)
                kill_job(bgjob, SIGKILL);
            else
            {
                kill_job(bgjob, SIGTERM);
                kill_job(bgjob, SIGCONT);
            }
        }

        if (!check_children())
            pause();
    }

    fprintf(stderr,"Done - exiting\n");
    return 0;
}


Go to the first, previous, next, last section, table of contents.