ment 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 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 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"' IRIX `"/bin/echo 'where\ndetach' | dbx -p %d"' Examples ******** Catching SIGCHLD ================ #include /* include this before any other sys headers */ #include /* header for waitpid() and various macros */ #include /* header for signal functions */ #include /* header for fprintf() */ #include /* 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()) { 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; /* 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 #include #include 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 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); } Reading the process table - SYSV version ======================================== 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; } Reading the process table - AIX 4.2 version =========================================== #include #include 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 ...]\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 ============================================ #include /* FILE, sprintf, fgets, puts */ #include /* atoi, exit, EXIT_SUCCESS */ #include /* strtok, strcmp */ #include /* pid_t */ #include /* 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[]) { puts(procname(atoi(argv[1]))); exit(EXIT_SUCCESS); } Daemon utility functions ======================== #include #include #include #include #include #include #include /* closeall() - close all FDs >= a specified value */ void closeall(int fd) { int fdlimit = sysconf(_SC_OPEN_MAX); while (fd < fdlimit) 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 */ 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 #include #include #include #include #include #include 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. */ 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 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; } ============================================================================== -- Andrew.