荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: georgehill (人生不定式), 信区: Linux
标  题: Unix Programming FAQ (v1.37)-Use of to(转寄)
发信站: BBS 荔园晨风站 (Thu Sep 14 13:02:41 2000), 站内信件

【 以下文字转载自 georgehill 的信箱 】
【 原文由 georgehill.bbs@smth.org 所发表 】
发信人: hhuu (什么是水,就是water啊), 信区: FreeBSD
标  题: Unix Programming FAQ (v1.37)-Use of to(转寄)
发信站: BBS 水木清华站 (Wed Sep 13 20:54:41 2000)

发信人: Luther (天語『至菜※努力』), 信区: Security
标  题: Unix Programming FAQ (v1.37)-Use of to(转寄)
发信站: 武汉白云黄鹤站 (Wed Sep 13 15:47:29 2000), 站内信件

6. Use of tools
***************
6.1 How can I debug the children after a fork?
==============================================
Depending on the tools available there are various ways:
Your debugger may have options to select whether to follow the parent or
the child process (or both) after a `fork()', which may be sufficient for
some purposes.
Alternatively, your debugger may have an option which allows you to attach
to a running process. This can be used to attach to the child process after
it has been started. If you don't need to examine the very start of the
child process, this is usually sufficient. Otherwise, you may wish to
insert a `sleep()' call after the `fork()' in the child process, or a loop
such as the following:
     {
         volatile int f = 1;
         while(f);
         while(f);
     }
which will hang the child process until you explicitly set `f' to 0 using
the debugger.
Remember, too, that actively using a debugger isn't the only way to find
errors in your program; utilities are available to trace system calls and
signals on many unix flavours, and verbose logging is also often useful.
6.2 How to build library from other libraries?
==============================================
Assuming we're talking about an archive (static) library, the easiest way
is to explode all the constituent libraries into their original objects
using `ar x' in an empty directory, and combine them all back together.  Of
course, there is the potential for collision of filenames, but if the
libraries are large, you probably don't want to be combining them in the
first place....
6.3 How to create shared libraries / dlls?
==========================================
The precise method for creating shared libraries varies between different
systems. There are two main parts to the process; firstly the objects to be
included in the shared library must be compiled, usually with options to
indicate that the code is to be position-independent; secondly, these
objects are linked together to form the library.
Here's a trivial example that should illustrate the idea:
Here's a trivial example that should illustrate the idea:
     /* file shrobj.c */

     const char *myfunc()
     {
         return "Hello World";
     }

     /* end shrobj.c */

     /* file hello.c */

     #include <stdio.h>

     extern const char *myfunc();

     main()
     {
         printf("%s\n", myfunc());
         return 0;
     }

     /* end hello.c */
     /* end hello.c */

     $ gcc -fpic -c shrobj.c
     $ gcc -shared -o libshared.so shrobj.o
     $ gcc hello.c libshared.so
     $ ./a.out
     Hello World
By far the best method if you want the library and build procedure to be
anything approaching portable is to use GNU Libtool.  This is a small suite
of utilities which know about the platform-dependent aspects of building
shared libraries; you can distribute the necessary bits with your program,
so that when the installer configures the package, he or she can decide
what libraries to build.  Libtool works fine on systems which do not
support shared libraries.  It also knows how to hook into GNU Autoconf and
GNU Automake (if you use those tools to manage your program's build
procedure).
If you don't want to use Libtool, then for compilers other than gcc, you
should change the compiler options as follows:
AIX 3.2 using xlc (unverified)
     Drop the `-fpic', and use `-bM:SRE -bE:libshared.exp' instead of
     `-shared'. You also need to create a file `libshared.exp' containing
     the list of symbols to export, in this case `myfunc'.  In addition,
     use `-e _nostart' when linking the library (on newer versions of AIX,
     use `-e _nostart' when linking the library (on newer versions of AIX,
     I believe this changes to `-bnoentry').
SCO OpenServer 5 using the SCO Development System (unverified)
     Shared libraries are only available on OS5 if you compile to ELF
     format, which requires the `-belf' option. Use `-Kpic' instead of
     `-fpic', and `cc -belf -G' for the link step.
Solaris using SparcWorks compilers
     Use `-pic' instead of `-fpic', and use `ld -G' instead of `gcc
     -shared'.
(Submission of additional entries for the above table is encouraged.)
Other issues to watch out for:
   * AIX and (I believe) Digital Unix don't require the -fpic option,
     because all code is position independent.
   * AIX normally requires that you create an `export file', which is a list
     of symbols to be exported from the shared library. Some versions of the
     linker (possibly only the SLHS linker, svld?) have an option to export
     all symbols.
   * If you want to refer to your shared library using the conventional
     `-l' parameter to the linker, you will have to understand how shared
     libraries are searched for at runtime on your system. The most common
     method is by using the `LD_LIBRARY_PATH' environment variable, but
     there is usually an additional option to specify this at link time.
   * Most implementations record the expected runtime location of the shared
   * Most implementations record the expected runtime location of the shared
     library internally. Thus, moving a library from one directory to
     another may prevent it from working. Many systems have an option to
     the linker to specify the expected runtime location (the `-R' linker
     option on Solaris, for example, or the `LD_RUN_PATH' environment
     variable).
   * ELF and a.out implementations may have a linker option `-Bsymbolic'
     which causes internal references within the library to be resolved.
     Otherwise, on these systems, all symbol resolution is deferred to the
     final link, and individual routines in the main program can override
     ones in the library.
6.4 Can I replace objects in a shared library?
==============================================
Generally, no.
On most systems (except AIX), when you link objects to form a shared
library, it's rather like linking an executable; the objects don't retain
their individual identity. As a result, it's generally not possible to
extract or replace individual objects from a shared library.
6.5 How can I generate a stack dump from within a running program?
==================================================================
Some systems provide library functions for unwinding the stack, so that you
can (for example) generate a stack dump in an error-handling function.
However, these are highly system-specific, and only a minority of systems
However, these are highly system-specific, and only a minority of systems
have them.
A possible workaround is to get your program to invoke a debugger *on
itself* - the details still vary slightly between systems, but the general
idea is to do this:
     void dump_stack(void)
     {
         char s[160];

         sprintf(s, "/bin/echo 'where\ndetach' | dbx -a %d", getpid());
         system(s);

         return;
     }
You will need to tweak the commands and parameters to dbx according to your
system, or even substitute another debugger such as `gdb', but this is
still the most general solution to this particular problem that I've ever
seen. Kudos to Ralph Corderoy for this one :-)
Here's a list of the command lines required for some systems:
Most systems using dbx
     `"/bin/echo 'where\ndetach' | dbx /path/to/program %d"'
AIX
     `"/bin/echo 'where\ndetach' | dbx -a %d"'
     `"/bin/echo 'where\ndetach' | dbx -a %d"'
IRIX
     `"/bin/echo 'where\ndetach' | dbx -p %d"'
Examples
********
Catching SIGCHLD
================
     #include <sys/types.h>  /* include this before any other sys headers */
     #include <sys/wait.h>   /* header for waitpid() and various macros */
     #include <signal.h>     /* header for signal functions */
     #include <stdio.h>      /* header for fprintf() */
     #include <unistd.h>     /* header for fork() */

     void sig_chld(int);     /* prototype for our SIGCHLD handler */

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

         /* Assign sig_chld as our SIGCHLD handler */
         act.sa_handler = sig_chld;


         /* We don't want to block any other signals in this example */
         sigemptyset(&act.sa_mask);

         /*
          * We're only interested in children that have terminated, not ones
          * which have been stopped (eg user pressing control-Z at terminal)
          */
         act.sa_flags = SA_NOCLDSTOP;

         /*
          * Make these values effective. If we were writing a real
          * application, we would probably save the old value instead of
          * passing NULL.
          */
         if (sigaction(SIGCHLD, &act, NULL) < 0)
         {
             fprintf(stderr, "sigaction failed\n");
             return 1;
         }

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

         case 0:                         /* child -- finish straight away */
             _exit(7);                   /* exit status = 7 */

         default:                        /* parent */
             sleep(10);                  /* give child time to finish */
         }

         return 0;
     }

     /*
      * The signal handler function -- only gets called when a SIGCHLD
      * is received, ie when a child terminates
      */
     void sig_chld(int signo)
     {
         int status, child_val;
         int status, child_val;

         /* Wait for any child without blocking */
         if (waitpid(-1, &status, WNOHANG) < 0)
         {
             /*
              * calling standard I/O functions like fprintf() in a
              * signal handler is not recommended, but probably OK
              * in toy programs like this one.
              */
             fprintf(stderr, "waitpid failed\n");
             return;
         }

         /*
          * We now have the info in 'status' and can manipulate it using
          * the macros in wait.h.
          */
         if (WIFEXITED(status))                /* did child exit normally? */
         {
             child_val = WEXITSTATUS(status); /* get child's exit status */
             printf("child's exited normally with status %d\n", child_val);
         }
         }
     }
Reading the process table - SUNOS 4 version
===========================================
     #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;
         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'){
                     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{
                 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);
     }
Reading the process table - SYSV version
========================================
     pid_t
     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{
                             else{
                                 close(fd);
                                 break;
                             }
                         }
                     }
                     close(fd);
                 }
             }
         }
         closedir(dp);
         return ourretval;
     }
Reading the process table - AIX 4.2 version
===========================================
     #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);
       }
     }
Reading the process table using popen and ps
Reading the process table using popen and 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;

        /* read the header line */
        if ((char *)0 == fgets(line, sizeof line, fp))
        {
        {
           pclose(fp);
           return (char *)0;
        }

        /* figure out where the command name is from the column headings.
         * (BSD-ish machines put the COMMAND in the 5th column, while SysV
         * seems to put CMD or COMMAND in the 4th column.)
         */
        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))
           { /*  we found the COMMAND column */
              cmd = token;
              break;
           }
        }


        /* read the ps(1) output line */
        if ((char *)0 == fgets(line, sizeof line, fp))
        {
           pclose(fp);
           return (char *)0;
        }

        /* grab the "word" underneath the command heading... */
        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[])
     int main(int argc, char *argv[])
     {
        puts(procname(atoi(argv[1])));
        exit(EXIT_SUCCESS);
     }
Daemon utility functions
========================
     #include <unistd.h>
     #include <stdlib.h>
     #include <fcntl.h>
     #include <signal.h>
     #include <sys/types.h>
     #include <sys/wait.h>
     #include <errno.h>

     /* closeall() -- close all FDs >= a specified value */

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

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

     /* daemon() - detach process from user and disappear into the background
      * returns -1 on failure, but you can't do much except exit in that case
      * since we may already have forked. This is based on the BSD version,
      * so the caller is responsible for things like the umask, etc.
      */

     /* believed to work on all Posix systems */

     int daemon(int nochdir, int noclose)
     {
         switch (fork())
         {
             case 0:  break;
             case -1: return -1;
             default: _exit(0);          /* exit the original process */
         }

         if (setsid() < 0)               /* shoudn't fail */
           return -1;


         /* dyke out this switch if you want to acquire a control tty in */
         /* the future -- not normally advisable for daemons */

         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() -- like fork, but the new process is immediately orphaned
      *            (won't leave a zombie when it exits)
      * Returns 1 to the parent, not any meaningful pid.
      * The parent cannot wait() for the new process (it's unrelated).
      */

     /* This version assumes that you *haven't* caught or ignored SIGCHLD. */
     /* If you have, then you should just be using fork() instead anyway.  */

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

         if (!(pid = fork()))
         {
             switch (fork())
             {
               case 0:  return 0;
               case -1: _exit(errno);    /* assumes all errnos are <256 */
               case -1: _exit(errno);    /* assumes all errnos are <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;  /* well, sort of :-) */

         return -1;
     }
An example of using the above functions:
     #include <sys/types.h>
     #include <sys/socket.h>
     #include <netinet/in.h>
     #include <stdio.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);
     }

     /* the actual child process is here. */
     /* the actual child process is here. */

     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);
     }

     /* This is the daemon's main work -- listen for connections and spawn */

     void process()
     {
         struct sockaddr_in addr;
         int addrlen = sizeof(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;
     }
Modem handling example
======================
     /* issue some simple modem commands
      * requires the name of a serial device (preferably a dial-out device,
      * or a non-modem-control device) as its only parameter.
      * If you don't have functional dial-out devices, then move CLOCAL
      * to CFLAGS_TO_SET instead.
      */

     #include <stdio.h>
     #include <stdlib.h>
     #include <fcntl.h>
     #include <unistd.h>
     #include <sys/types.h>
     #include <sys/time.h>
     #include <sys/ioctl.h>   /* maybe; system-dependent */
     #include <termios.h>
     #include <errno.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 };

     /* system-dependent */
     #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);

         /* set vaguely sensibe settings */


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

         /* no special input or output processing */

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

         /* set 8-bit character size and miscellanous control modes */

         attr.c_cflag &= ~(CSIZE | CFLAGS_TO_CLEAR | CFLAGS_HARDFLOW);
         attr.c_cflag |= (CS8 | CFLAGS_TO_SET);
         if (flow == HardFlow)
             attr.c_cflag |= CFLAGS_HARDFLOW;

         /* local modes */

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

         /* special characters -- most disabled by prior settings anyway */

         {
         {
             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;

         /* timing controls for read() */

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

         /* baud rate */

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

         /* write settings */

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

         /* turn off O_NONBLOCK if the device remembered it */

         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;
     }

     /* some simple timing utilities */

     /* add SECS and USECS to *TV */

     static void timeradd(struct timeval *tv, long secs, long 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;
         }
     }

     /* Set *RES = *A - *B, returning the sign of the result */

     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;
         res->tv_usec = usec;

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


     /* this doesn't try and cope with pathological strings (e.g. ababc)
      * timeout is in millisecs
      * A more usual approach to this is to use alarm() for the timeout.
      * This example avoids signal handling for simplicity and to illustrate
      * an alternative approach
      */

     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);
         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;
                 ++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;
     }
     }
Job Control example
===================
     /* functions to spawn foreground/background jobs */

     #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>


     /* Some of the functions below fail if the control tty can't be
      * located, or if the calling process isn't in the foreground. In the
      * first case, we are assuming that a foreground process will have the
      * ctty open on either stdin, stdout or stderr, and we return ENOTTY
      * if it isn't. In the second case, we return EPERM if a
      * non-foreground process attempts to put something into the
      * foreground (probably overly paranoid) except for the special case
      * of foreground_self().
      * of foreground_self().
      */


     /* assign the terminal (open on ctty) to a specific pgrp. This wrapper
      * around tcsetpgrp() is needed only because of extreme bogosity on the
      * part of POSIX; conforming systems deliver STGTTOU if tcsetpgrp is
      * called from a non-foreground process (which it almost invariably is).
      * A triumph of spurious consistency over common sense.
      */

     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;
     }


     /* Like fork(), but does job control. FG is true if the newly-created
      * process is to be placed in the foreground. (This implicitly puts
      * the calling process in the background, so watch out for tty I/O
      * after doing this.) PGRP is -1 to create a new job, in which case
      * the returned pid is also the pgrp of the new job, or specifies an
      * existing job in the same session (normally used only for starting
      * second or subsequent process in a pipeline).  */

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

         /* If spawning a *new* foreground job, require that at least one
          * of stdin, stdout or stderr refer to the control tty, and that
          * the current process is in the foreground.
          * the current process is in the foreground.
          * Only check for controlling tty if starting a new foreground
          * process in an existing job.
          * A session without a control tty can only have background jobs
          */

         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 failure */
                 return pid;
                 return pid;

             case 0: /* child */

                 /* establish new process group, and put ourselves in
                  * foreground if necessary
                  * unclear what to do if setpgid fails ("can't happen")
                  */

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

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

                 return 0;

             default: /* parent */

                 /* establish child process group here too. */

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

                 setpgid(pid, pgrp);

                 return pid;
         }

         /*NOTREACHED*/
     }


     /* Kill job PGRP with signal SIGNO */

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


     /* Suspend job PGRP */

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


     /* Resume job PGRP in background */

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


     /* resume job PGRP in foreground */

     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)
             && (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);
     }


     /* put ourselves in the foreground, e.g. after suspending a foreground
      * job
      */

     int foreground_self()
     {
         pid_t curpgrp;
         int ctty;

         if ((curpgrp = tcgetpgrp(ctty = 2)) < 0
         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() - close all FDs >= a specified value */

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

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


     /* like system(), but executes the specified command as a background
      * job, returning the pid of the shell process (which is also the pgrp
      * of the job, suitable for kill_job etc.)
      * of the job, suitable for kill_job etc.)
      * If INFD, OUTFD or ERRFD are non-NULL, then a pipe will be opened and
      * a descriptor for the parent end of the relevent pipe stored there.
      * If any of these are NULL, they will be redirected to /dev/null in the
      * child.
      * Also closes all FDs > 2 in the child process (an oft-overlooked task)
      */

     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)
         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 failure */
                     error = errno;
                     break;


                 case 0: /* child proc */

                     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: /* parent proc */

                     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;
                     return pid;
             }
         }

         /* only reached if error */

         {
             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;
     }


     /*--------------------------------------------------------------------*/
     /* This bit is a somewhat trivial example of using the above.         */
     /* This bit is a somewhat trivial example of using the above.         */

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

     #ifndef WCOREDUMP
      /* If WCOREDUMP is missing, you might want to supply a correct
       * definition for your platform (this is usually (status & 0x80) but
       * not always) or punt (as in this example) by assuming no core dumps.
       */
     # 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;
                 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");
         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())
                 else
                 pause();
         }

         fprintf(stderr,"Done - exiting\n");
         return 0;
     }
==============================================================================
--
Andrew.
--
║ ◇ 系統 ◇ ║ ¤ FreeBSD ¤ ║ --=【紅色小魔鬼】=-- ║ Welcome ║

※ 来源:.武汉白云黄鹤站 bbs.whnet.edu.cn.[FROM: 202.112.20.201]
--
※ 转载:.武汉白云黄鹤站 bbs.whnet.edu.cn.[FROM: 202.112.20.201]

--
  行列中        作不朽文章
  谈笑间        论古今英雄
  痴狂里        诉红尘情爱
  来去时        不枉一生风流
                        ---------  bbs.ntu.edu.tw


※ 来源:·BBS 水木清华站 smth.org·[FROM: 166.111.160.6]
--
※ 转载:·BBS 荔园晨风站 bbs.szu.edu.cn·[FROM: 192.168.1.115]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店