Professional Documents
Culture Documents
*****************
1. Process Control
1.1 Creating new processes: fork()
1.1.1 What does fork() do?
1.1.2 What's the difference between fork() and vfork()?
1.1.3 Why use _exit rather than exit in the child branch of a
fork?
1.2 Environment variables
1.2.1 How can I get/set an environment variable from a program?
1.2.2 How can I read the whole environment?
1.3 How can I sleep for less than a second?
1.4 How can I get a finer-grained version of alarm()?
1.5 How can a parent and child process communicate?
1.6 How do I get rid of zombie processes?
1.6.1 What is a zombie?
1.6.2 How do I prevent them from occuring?
1.7 How do I get my program to act like a daemon?
1.8 How can I look at process in the system like ps does?
1.9 Given a pid, how can I tell if it's a running program?
1.10 What's the return value of system/pclose/waitpid?
1.11 How do I find out about a process' memory usage?
1.12 Why do processes never decrease in size?
1.13 How do I change the name of my program (as seen by `ps')?
1.14 How can I find a process' executable file?
1.14.1 So where do I put my configuration files then?
1.15 Why doesn't my process get SIGHUP when its parent dies?
1.16 How can I kill all descendents of a process?
3. Terminal I/O
3.1 How can I make my program not echo input?
3.2 How can I read single characters from the terminal?
3.3 How can I check and see if a key was pressed?
3.4 How can I move the cursor around the screen?
3.5 What are pttys?
3.6 How to handle a serial port or modem?
3.6.1 Serial device names and types
3.6.2 Setting up termios flags
3.6.2.1 c_iflag
3.6.2.2 c_oflag
3.6.2.3 c_cflag
3.6.2.4 c_lflag
3.6.2.5 c_cc
4. System Information
4.1 How can I tell how much memory my system has?
4.2 How do I check a user's password?
4.2.1 How do I get a user's password?
4.2.2 How do I get shadow passwords by uid?
4.2.3 How do I verify a user's password?
5. Miscellaneous programming
5.1 How do I compare strings using wildcards?
5.1.1 How do I compare strings using filename patterns?
5.1.2 How do I compare strings using regular expressions?
5.2 What's the best way to send mail from a program?
5.2.1 The simple method: /bin/mail
5.2.2 Invoking the MTA directly: /usr/lib/sendmail
5.2.2.1 Supplying the envelope explicitly
5.2.2.2 Allowing sendmail to deduce the recipients
6. Use of tools
6.1 How can I debug the children after a fork?
6.2 How to build library from other libraries?
6.3 How to create shared libraries / dlls?
6.4 Can I replace objects in a shared library?
6.5 How can I generate a stack dump from within a running program?
1. Process Control
******************
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
case 0:
/* pid of zero is the child */
/* Here we're the child...what should we do? */
/* ... */
/* but after doing it, we should do something like: */
_exit(0);
default:
/* pid greater than zero is parent getting the child's pid */
printf("Child's pid is %d\n",pid);
}
Of help when doing this is knowing just what is and is not inherited
by the
child. This list can vary depending on Unix implementation, so take
it
with a grain of salt. Note that the child gets *copies* of these
things,
not the real thing.
* environment
* stack
* memory
* close-on-exec flags
* nice value
* scheduler class
* process group ID
* session ID
* root directory
* resource limits
* controlling terminal
* process ID
* process, text, data and other memory locks are NOT inherited.
The basic difference between the two is that when a new process is
created
with `vfork()', the parent process is temporarily suspended, and the
child
process might borrow the parent's address space. This strange state of
affairs continues until the child process either exits, or calls
`execve()', at which point the parent process continues.
1.1.3 Why use _exit rather than exit in the child branch of a fork?
-------------------------------------------------------------------
There are a few differences between `exit()' and `_exit()' that become
significant when `fork()', and especially `vfork()', is used.
The basic difference between `exit()' and `_exit()' is that the former
performs clean-up related to user-mode constructs in the library, and
calls
user-supplied cleanup functions, whereas the latter performs only the
kernel cleanup for the process.
#include <stdlib.h>
#include <stdlib.h>
Suppose you wanted to get the value for the `TERM' environment
variable.
You would use this code:
char *envvar;
envvar=getenv("TERM");
sprintf(envbuf,"MYVAR=%s","MYVAL");
if(putenv(envbuf))
{
printf("Sorry, putenv() couldn't find the memory for %
s\n",envbuf);
/* Might exit() or something here if you can't live without
it */
}
If you don't know the names of the environment variables, then the
`getenv()' function isn't much use. In this case, you have to dig
deeper
into how the environment is stored.
#include <stdio.h>
int main()
{
char **ep = environ;
char *p;
while ((p = *ep++))
printf("%s\n", p);
return 0;
}
#include <stdio.h>
* If your system has itimers (most do), you can roll your own
`usleep()'
using them (see the BSD sources for `usleep()' for how to do
this)
`ITIMER_REAL'
counts real (wall clock) time, and sends the `SIGALRM' signal
`ITIMER_VIRTUAL'
counts process virtual (user CPU) time, and sends the `SIGVTALRM'
signal
`ITIMER_PROF'
counts user and system CPU time, and sends the `SIGPROF' signal;
it is
intended for interpreters to use for profiling.
A parent and child can communicate through any of the normal inter-
process
communication schemes (pipes, sockets, message queues, shared
memory), but
also have some special ways to communicate that take advantage of
their
relationship as a parent and child.
One of the most obvious is that the parent can get the exit status of
the
child.
Since the child inherits file descriptors from its parent, the parent
can
open both ends of a pipe, fork, then the parent close one end and the
child
close the other end of the pipe. This is what happens when you call
the
`popen()' routine to run another program from within yours, i.e. you
can
write to the file descriptor returned from `popen()' and the child
process
sees it as its stdin, or you can read from the file descriptor and
see what
the program wrote to its stdout. (The mode parameter to `popen()'
defines
which; if you want to do both, then you can do the plumbing yourself
without too much difficulty.)
When a program forks and the child finishes before the parent, the
kernel
still keeps some of its information about the child in case the parent
might need it - for example, the parent may need to check the child's
exit
status. To be able to get this information, the parent calls `wait
()';
when this happens, the kernel can discard the information.
In the interval between the child terminating and the parent calling
`wait()', the child is said to be a `zombie'. (If you do `ps', the
child
will have a `Z' in its status field to indicate this.) Even though
it's
not running, it's still taking up an entry in the process table. (It
consumes no other resources, but some utilities may show bogus
figures for
e.g. CPU usage; this is because some parts of the process table entry
have
been overlaid by accounting info to save space.)
This is not good, as the process table has a fixed number of entries
and it
is possible for the system to run out of them. Even if the system
doesn't
run out, there is a limit on the number of processes each user can
run,
which is usually smaller than the system's limit. This is one of the
reasons why you should always check if `fork()' failed, by the way!
You need to ensure that your parent process calls `wait()' (or
`waitpid()',
`wait3()', etc.) for every child process that terminates; or, on some
systems, you can instruct the system that you are uninterested in
child
exit states.
The other technique is to catch the SIGCHLD signal, and have the
signal
handler call `waitpid()' or `wait3()'. See the examples section for a
complete program.
In the examples section, there are two complete versions of this; one
for
SunOS 4, which requires root permission to run and uses the `kvm_*'
routines to read the information from kernel data structures; and
another
for SVR4 systems (including SunOS 5), which uses the `/proc'
filesystem.
* `kill()' returns 0
- this implies that a process exists with the given PID, and
the
system would allow you to send signals to it. It is
system-dependent whether the process could be a zombie.
The man page is right, and so are you! If you read the man page for
`waitpid()' you'll find that the return code for the process is
encoded.
The value returned by the process is normally in the top 16 bits, and
the
rest is used for other things. You can't rely on this though, not if
you
want to be portable, so the suggestion is that you use the macros
provided.
These are usually documented under `wait()' or `wstat'.
`WIFEXITED(stat)'
Non zero if child exited normally.
`WEXITSTATUS(stat)'
exit code returned by child
`WIFSIGNALED(stat)'
Non-zero if child was terminated by a signal
`WTERMSIG(stat)'
signal number that terminated child
`WIFSTOPPED(stat)'
non-zero if child is stopped
`WSTOPSIG(stat)'
number of signal that stopped child
`WIFCONTINUED(stat)'
non-zero if status was for continued child
`WCOREDUMP(stat)'
If `WIFSIGNALED(stat)' is non-zero, this is non-zero if the
process
left behind a core dump.
When you free memory back to the heap with `free()', on almost all
systems
that *doesn't* reduce the memory usage of your program. The memory
`free()'d is still part of the process' address space, and will be
used to
satisfy future `malloc()' requests.
If you really need to free memory back to the system, look at using
`mmap()' to allocate private anonymous mappings. When these are
unmapped,
the memory really is released back to the system. Certain
implementations
of `malloc()' (e.g. in the GNU C Library) automatically use `mmap()'
where
available to perform large allocations; these blocks are then
returned to
the system on `free()'.
On BSDish systems, the `ps' program actually looks into the address
space
of the running process to find the current `argv[]', and displays
that.
That enables a program to change its `name' simply by modifying `argv
[]'.
On SysVish systems, the command name and usually the first 80 bytes
of the
parameters are stored in the process' u-area, and so can't be directly
modified. There may be a system call to change this (unlikely), but
otherwise the only way is to perform an `exec()', or write into kernel
memory (dangerous, and only possible if running as root).
The most common reason people ask this question is in order to locate
configuration files with their program. This is considered to be bad
form;
directories containing executables should contain *nothing* except
executables, and administrative requirements often make it desirable
for
configuration files to be located on different filesystems to
executables.
1.15 Why doesn't my process get SIGHUP when its parent dies?
============================================================
There isn't a fully general approach to doing this. While you can
determine the relationships between processes by parsing `ps' output,
this
is unreliable in that it represents only a snapshot of the system.
`http://www.lcg.org/sock-faq/'
fd_set set;
FD_ZERO(&set); /* empties the set */
FD_SET(fd,&set); /* adds FD to the set */
FD_CLR(fd,&set); /* removes FD from the set */
FD_ISSET(fd,&set) /* true if FD is in the set */
where
`nfds'
the number of FDs to examine; this must be greater than the
largest FD
in any of the fdsets, *not* the actual number of FDs specified
`readset'
the set of FDs to examine for readability
`writeset'
the set of FDs to examine for writability
`exceptfds'
the set of FDs to examine for exceptional status (note: errors
are
*not* exceptional statuses)
`timeout'
NULL for infinite timeout, or points to a timeval specifying the
maximum wait time (if `tv_sec' and `tv_usec' both equal zero,
then the
status of the FDs is polled, but the call never blocks)
The call returns the number of `ready' FDs found, and the three
fdsets are
modified in-place, with only the ready FDs left in the sets. Use the
`FD_ISSET' macro to test the returned sets.
Here's a simple example of testing a single FD for readability:
FD_ZERO(&fds);
FD_SET(fd,&fds);
tv.tv_sec = tv.tv_usec = 0;
return FD_ISSET(fd,&fds) ? 1 : 0;
}
Note that we can pass `NULL' for fdsets that we aren't interested in
testing.
struct pollfd {
int fd; /* The descriptor. */
short events; /* The event(s) is/are specified here. */
short revents; /* Events found are returned here. */
};
A lot like `select()', the return value if positive reflects how many
descriptors were found to satisfy the events requested. A zero return
value is returned if the timeout period is reached before any of the
events
specified have occured. A negative value should immediately be
followed by
a check of `errno', since it signifies an error.
Here's an example:
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <stropts.h>
#include <poll.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#define NORMAL_DATA 1
#define HIPRI_DATA 2
poll_list[0].fd = fd1;
poll_list[1].fd = fd2;
poll_list[0].events = POLLIN|POLLPRI;
poll_list[1].events = POLLIN|POLLPRI;
while(1)
{
retval = poll(poll_list,(unsigned long)2,-1);
/* Retval will always be greater than 0 or -1 in this
case.
Since we're doing it while blocking */
if(retval < 0)
{
fprintf(stderr,"Error while polling: %s\n",strerror
(errno));
return -1;
}
if(((poll_list[0].revents&POLLHUP) == POLLHUP) ||
((poll_list[0].revents&POLLERR) == POLLERR) ||
((poll_list[0].revents&POLLNVAL) == POLLNVAL) ||
((poll_list[1].revents&POLLHUP) == POLLHUP) ||
((poll_list[1].revents&POLLERR) == POLLERR) ||
((poll_list[1].revents&POLLNVAL) == POLLNVAL))
return 0;
if((poll_list[0].revents&POLLIN) == POLLIN)
handle(poll_list[0].fd,NORMAL_DATA);
if((poll_list[0].revents&POLLPRI) == POLLPRI)
handle(poll_list[0].fd,HIPRI_DATA);
if((poll_list[1].revents&POLLIN) == POLLIN)
handle(poll_list[1].fd,NORMAL_DATA);
if((poll_list[1].revents&POLLPRI) == POLLPRI)
handle(poll_list[1].fd,HIPRI_DATA);
}
}
2.1.3 Can I use SysV IPC at the same time as select or poll?
------------------------------------------------------------
- `fork()', and have the child process handle the SysV IPC,
communicating with the parent process by a pipe or socket, which
the
parent process can `select()' on.
2.2 How can I tell when the other end of a connection shuts down?
=================================================================
If you try to read from a pipe, socket, FIFO etc. when the writing
end of
the connection has been closed, you get an end-of-file indication
(`read()'
returns 0 bytes read). If you try and write to a pipe, socket etc.
when the
reading end has closed, then a `SIGPIPE' signal will be delivered to
the
process, killing it unless the signal is caught. (If you ignore or
block
the signal, the `write()' call fails with `EPIPE'.)
2.4 How can I find out if someone else has a file open?
=======================================================
This is, in general, too hard to do anyway. Tools like `fuser' and
`lsof'
that find out about open files do so by grovelling through kernel data
structures in a most unhealthy fashion. You can't usefully invoke
them from
a program, either, because by the time you've found out that the file
is/isn't open, the information may already be out of date.
There are three main file locking mechanisms available. All of them
are
`advisory'[*], which means that they rely on programs co-operating in
order
to work. It is therefore vital that all programs in an application
should
be consistent in their locking regime, and great care is required
when your
programs may be sharing files with third-party software.
[*] Well, actually some Unices permit mandatory locking via the sgid
bit -
RTFM for this hack.
flock();
lockf();
fcntl();
`flock()' originates with BSD, and is now available in most (but not
all)
Unices. It is simple and effective on a single host, but doesn't
work at
all with NFS. It locks an entire file. Perhaps rather deceptively,
the
popular Perl programming language implements its own `flock()' where
necessary, conveying the illusion of true portability.
lock(fd);
write_to(some_function_of(fd));
flush_output_to(fd); /* NEVER unlock while output may be
buffered */
unlock(fd);
do_something_else; /* another process might update it */
lock(fd);
seek(fd, somewhere); /* because our old file pointer is not
safe */
do_something_with(fd);
...
#include <fcntl.h>
#include <unistd.h>
2.6 How do I find out if a file has been updated by another process?
====================================================================
If you want more detail about how it works, then the simple answer is:
The following routine illustrates how to use `stat()' to get the file
size.
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
if(stat(path,&file_stats))
return -1;
*size = file_stats.st_size;
return 0;
}
if (path.length() == 1 || pos == 1)
{
pfx = getenv("HOME");
if (!pfx)
{
// Punt. We're trying to expand ~/, but HOME isn't
set
struct passwd *pw = getpwuid(getuid());
if (pw)
pfx = pw->pw_dir;
}
}
else
{
string user(path,1,(pos==string::npos) ? string::npos :
pos-1);
struct passwd *pw = getpwnam(user.c_str());
if (pw)
pfx = pw->pw_dir;
}
if (!pfx)
return path;
string result(pfx);
if (pos == string::npos)
return result;
result += path.substr(pos+1);
return result;
}
/* set the umask explicitly, you don't know where it's been */
umask(0);
if (mkfifo("test_fifo", S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP))
{
perror("mkfifo");
exit(1);
}
/* set the umask explicitly, you don't know where it's been */
umask(0);
if (mknod("test_fifo",
S_IFIFO | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
0))
{
perror("mknod");
exit(1);
}
To use the pipe, you open it like a normal file, and use `read()' and
`write()' just as though it was a plain pipe.
However, the `open()' of the pipe may block. The following rules
apply:
* If you open for both reading and writing (`O_RDWR'), then the
open
will not block.
* If you open for reading (`O_RDONLY'), the open will block until
another process opens the FIFO for writing, unless `O_NONBLOCK'
is
specified, in which case the open succeeds.
* If you open for writing `O_WRONLY', the open will block until
another
process opens the FIFO for reading, unless `O_NONBLOCK' is
specified,
in which case the open fails.
When reading and writing the FIFO, the same considerations apply as
for
regular pipes and sockets, i.e. `read()' will return EOF when all
writers
have closed, and `write()' will raise `SIGPIPE' when there are no
readers.
If `SIGPIPE' is blocked or ignored, the call fails with `EPIPE'.
How can I implement two way communication between one server and
several clients?
However, the server can not use a single pipe to communicate with the
clients. If more than one client is reading the same pipe, there is
no way
to ensure that the appropriate client receives a given response.
A solution is to have the client create its own incoming pipe before
sending data to the server, or to have the server create its outgoing
pipes
after receiving data from the client.
3. Terminal I/O
***************
How can I make my program not echo input, like login does when
asking
for your password?
#include <stdlib.h>
#include <stdio.h>
#include <termios.h>
#include <string.h>
void echo_off(void)
{
struct termios new_settings;
tcgetattr(0,&stored_settings);
new_settings = stored_settings;
new_settings.c_lflag &= (~ECHO);
tcsetattr(0,TCSANOW,&new_settings);
return;
}
void echo_on(void)
{
tcsetattr(0,TCSANOW,&stored_settings);
return;
}
#include <stdlib.h>
#include <stdio.h>
#include <termios.h>
#include <string.h>
void set_keypress(void)
{
struct termios new_settings;
tcgetattr(0,&stored_settings);
new_settings = stored_settings;
tcsetattr(0,TCSANOW,&new_settings);
return;
}
void reset_keypress(void)
{
tcsetattr(0,TCSANOW,&stored_settings);
return;
}
How can I check and see if a key was pressed? On DOS I use the
`kbhit()' function, but there doesn't seem to be an equivalent?
The basic steps for opening and initialising a serial device are:
* `open()' the device; this may require the use of certain flags:
`O_NONBLOCK'
Opening a dial-in or modem-controlled device will block
until
carrier is present, unless this flag is used. A nonblocking
open
gives you the opportunity to disable the modem controls (see
CLOCAL below) if necessary.
`O_NOCTTY'
On 4.4BSD-derived systems this is redundant, but on other
systems
it controls whether the serial device can become a control
terminal for the session. In most cases you probably
*don't* want
to acquire a control terminal, and should therefore specify
this
flag, but there are exceptions.
* You may wish, if you used `O_NONBLOCK' when opening the port, to
use
`fcntl()' to ensure that `O_NONBLOCK' is turned off again.
Systems
seem to differ as to whether a nonblocking open on a tty will
affect
subsequent `read()' calls; better to be explicit.
Once you have opened and set up the port, you can then use `read()'
and
`write()' normally. Note that the behaviour of `read()' will be
controlled
by the flag settings you gave to `tcsetattr()'.
The device names used for serial port devices vary quite widely
between
systems. Some examples from different systems are:
The precise interaction between the device name used, and the effect
on any
hardware handshake lines is system-, configuration- and hardware-
dependant,
but will usually follow approximately these rules (assuming that the
hardware is RS-232 DTE):
Some hints on setting up the termios flags when using a serial device
that
you've opened yourself (as opposed to using your existing control
tty):
3.6.2.1 c_iflag
...............
You probably want to set *all* the bits in `c_iflag' to 0, unless you
want
to use software flow control (ick) in which case you set `IXON' and
`IXOFF'.
3.6.2.2 c_oflag
...............
Most of the bits of `c_oflag' are hacks of one sort or another to make
output to slow terminals work, and as such some newer systems have
dropped
almost all of them as obsolete (especially all the gory output-padding
options). As with `c_iflag', setting everything to 0 is reasonable
for most
applications.
3.6.2.3 c_cflag
...............
If you need to generate even parity, then set `PARENB' and clear
`PARODD';
if you need to generate odd parity then set both `PARENB' and
`PARODD'. If
you don't want parity at all, then make sure `PARENB' is clear.
Clear `CSTOPB' unless you actually need to generate two stop bits.
3.6.2.4 c_lflag
...............
3.6.2.5 c_cc
............
Many of the slots in `c_cc' are only used if some other combination of
flags is set:
`VMIN' and `VTIME' (which may share slots with `VEOF' and `VEOL'
respectively, depending on the implementation) have the following
meaning.
The value of `VTIME' is (if not 0) always interpreted as a timer in
tenths
of seconds.
4. System Information
*********************
For HP-UX (9 and 10), the following code has been contributed:
Though this has changed with time, now user information may be kept on
other hosts, or not necessarily in the `/etc/passwd' file. Modern
implementations also made use of `shadow' password files which hold
the
password, along with sensitive information. This file would be
readable
only by privileged users.
#include <stdlib.h>
#include <stdio.h>
#include <pwd.h>
#include <shadow.h>
return shadow;
}
The problem is, that some systems do not keep the uid, or other
information
in the shadow database.
The most popular way is to have a one way encryption algorithm, where
the
password cannot be decrypted. Instead the password is taken in clear
text
from input, and encrypted and checked against the encrypted password
in the
database. The details of how to encrypt should really come from your
man
page for `crypt()', but here's a usual version:
This works because the salt used in encrypting the password is stored
as an
initial substring of the encrypted value.
5. Miscellaneous programming
****************************
*Filename patterns*
These are what the shell uses for filename expansion
(`globbing').
*Regular Expressions*
These are used by editors, `grep', etc. for matching text, but
they
normally *aren't* applied to filenames.
Unless you are unlucky, your system should have a function `fnmatch
()' to
do filename matching. This generally allows only the Bourne shell
style of
pattern; i.e. it recognises `*', `[...]' and `?', but probably won't
support the more arcane patterns available in the Korn and Bourne-
Again
shells.
If you don't have this function, then rather than reinvent the wheel,
you
are probably better off snarfing a copy from the BSD or GNU sources.
Also, for the common cases of matching actual filenames, look for
`glob()',
which will find all existing files matching a pattern.
One library available for this is the `rx' library, available from
the GNU
mirrors. This seems to be under active development, which may be a
good or
a bad thing depending on your point of view :-)
There are several ways to send email from a Unix program. Which is
the best
method to use in a given situation varies, so I'll present two of
them. A
third possibility, not covered here, is to connect to a local SMTP
port (or
a smarthost) and use SMTP directly; see RFC 821.
5.2.1 The simple method: /bin/mail
----------------------------------
#include <stdio.h>
int main()
{
FILE *mail = popen(MAILPROG " -s 'Test Message' root", "w");
if (!mail)
{
perror("popen");
exit(1);
}
if (pclose(mail))
{
fprintf(stderr, "mail failed!\n");
exit(1);
}
}
These methods can be extended to more complex cases, but there are
many
pitfalls to watch out for:
Here's an example:
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sysexits.h>
/* #include <paths.h> if you have it */
#ifndef _PATH_SENDMAIL
#define _PATH_SENDMAIL "/usr/lib/sendmail"
#endif
while (recipients[num_recip])
++num_recip;
if (!num_recip)
return 0; /* sending to no recipients is successful */
/* fork */
/* Plumbing */
if (fd != STDIN_FILENO)
dup2(fd, STDIN_FILENO);
/* go for it: */
execv(_PATH_SENDMAIL, argvec);
_exit(EX_OSFILE);
default: /* parent */
free(argvec);
rc = waitpid(pid, &status, 0);
if (rc < 0)
return -1;
if (WIFEXITED(status))
return WEXITSTATUS(status);
return -1;
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
/* #include <paths.h> if you have it */
#ifndef _PATH_SENDMAIL
#define _PATH_SENDMAIL "/usr/lib/sendmail"
#endif
char tfilename[L_tmpnam];
char command[128+L_tmpnam];
void cleanup(void)
{
unlink(tfilename);
}
if (argc < 2)
{
fprintf(stderr, "usage: %s recipients...\n", argv[0]);
exit(2);
}
if (tmpnam(tfilename) == NULL
|| (msg = fopen(tfilename,"w")) == NULL)
exit(2);
atexit(cleanup);
fclose(msg);
msg = fopen(tfilename,"a");
if (!msg)
exit(2);
/* Subject */
/* MIME stuff */
fputc('\n',msg);
fclose(msg);
/* invoke mailer */
return 0;
}
6. Use of tools
***************
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);
}
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.
/* file shrobj.c */
const char *myfunc()
{
return "Hello World";
}
/* end shrobj.c */
/* file hello.c */
#include <stdio.h>
main()
{
printf("%s\n", myfunc());
return 0;
}
/* end hello.c */
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 and (I believe) Digital Unix don't require the -fpic option,
because all code is position independent.
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?
==================================================================
void dump_stack(void)
{
char s[160];
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 :-)
AIX
`"/bin/echo 'where\ndetach' | dbx -a %d"'
IRIX
`"/bin/echo 'where\ndetach' | dbx -p %d"'
Examples
********
Catching SIGCHLD
================
int main()
{
struct sigaction act;
pid_t pid;
/*
* 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;
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;
/*
* 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);
}
}
#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);
}
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;
}
#include <stdio.h>
#include <procinfo.h>
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;
}
if(argc == 1)
{
printf("syntax: %s <program> [program ...]\n",argv[0]);
exit(1);
}
return token;
}
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
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;
}
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);
}
}
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);
fclose(out);
}
if (rc < 0)
errexit("setsockopt");
addr.sin_family = AF_INET;
addr.sin_port = htons(TCP_PORT);
addr.sin_addr.s_addr = INADDR_ANY;
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);
}
process();
return 0;
}
#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 <string.h>
#include <ctype.h>
/* system-dependent */
#define CFLAGS_HARDFLOW (CRTSCTS)
/* local modes */
{
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;
attr.c_cc[VMIN] = 1;
attr.c_cc[VTIME] = 0;
/* baud rate */
cfsetispeed(&attr, baud);
cfsetospeed(&attr, baud);
/* write settings */
return fd;
}
if (usec < 0)
usec += 1000000, --sec;
res->tv_sec = sec;
res->tv_usec = usec;
gettimeofday(&end, NULL);
timeradd(&end, timeo/1000, timeo%1000);
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 (c == str[matchlen])
++matchlen;
else
matchlen = 0;
}
return 0;
}
if (argc < 2)
die("no port specified");
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>
sigemptyset(&sigs);
sigaddset(&sigs,SIGTTOU);
sigprocmask(SIG_BLOCK, &sigs, &oldsigs);
rc = tcsetpgrp(ctty, pgrp);
return rc;
}
if (fg)
{
pid_t curpgrp;
case 0: /* child */
if (pgrp < 0)
pgrp = getpid();
return 0;
default: /* parent */
if (pgrp < 0)
pgrp = pid;
setpgid(pid, pgrp);
return pid;
}
/*NOTREACHED*/
}
if (curpgrp != getpgrp())
return errno = EPERM, (pid_t)-1;
int foreground_self()
{
pid_t curpgrp;
int ctty;
if (!cmd)
return errno = EINVAL, -1;
if (!error)
{
pid_t pid = spawn_job(0, -1);
switch (pid)
{
case -1: /* fork failure */
error = errno;
break;
case 0: /* child proc */
execl("/bin/sh","sh","-c",cmd,(char*)NULL);
_exit(127);
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);
/*---------------------------------------------------------------
-----*/
/* This bit is a somewhat trivial example of using the
above. */
#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;
++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;
}
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);
if(!check_children())
pause();
}
fprintf(stderr,"Done - exiting\n");
return 0;
}