WARNING: Programmer discretion is advised.
Post contains system programming, C, terminated processes.
Please don’t play with systems programming unless you know what you are doing.
As much as “Force Quit” is useful in OSX, sometimes it doesn’t do the job properly, and you may be forced to look a little deeper using the command line. While is it easy to terminate a process using the Unix command kill
, it is sometimes less than easy to find the actual process ID. In “The UNIX Programming Environment”, Kernighan and Pike introduce a program called zap
, written in C, which is an “interactive process killer”. I have reproduced the code below with a few fixes/tweaks from the original. It is a good example of how C can be used for some basic systems processing, and why it is such a powerful language in this realm.
The basic pretext for the program is the use of the Unix command ps
, which shows active processes. Used without any flags, ps
just returns a list of processes running from controlling terminals, i.e. applications won’t be shown. To do this we need to to use the -ax
flag, which will show all processes running including those which do not have a controlling terminal. The program uses the function popen()
, which is similar to fopen() except that it opens a “process”. The program zap.c
is shown below. The process to be searched for, is provided as an argument to zap
, e.g. zap firefox
.
#include <stdio.h>
#include <signal.h>
#include <string.h>
char *progname;
char *ps = "ps -ax";
int main(int argc, char *argv[]){
FILE *fin;
char buf[BUFSIZ];
char rsp;
int pid;
progname = argv[0];
if ((fin = popen(ps, "r")) == NULL){
printf("%s: can't run %s\n", progname, ps);
return(1);
}
fgets(buf, sizeof(buf), fin);
while (fgets(buf, sizeof(buf), fin) != NULL)
if (argc == 1 || strstr(buf, argv[1]) != NULL) {
buf[strlen(buf)-1] = '\0';
printf("force quit: %s? [y/n]", buf);
scanf(" %c",&rsp);
if (rsp == 'y'){
sscanf(buf, "%d", &pid);
printf("terminating process %d\n", pid);
kill(pid, SIGKILL);
}
}
return(0);
}
The code on lines 14-17 checks to see if popen()
can open the command “ps -ax
“. If it can’t do this, an error is generated. Line 18 just reads and discards the header line generated by “ps -ax
“. The loop in line 19 iterates through each line in the output. If the line (buf
) contains the search string (argv[1]
), then the line is further processed (line 20). Line 21 suppresses the '\n'
. Lines 22-23 prompts the user to indicate whether the process is to be terminated. If the response is yes (line 24), the first value in the line, an integer representing the process-id is extracted from the line (buf
), and stored in the variable pid
(line 25). Finally, the process is terminated using the function kill()
from the library signal.h
(line 27).
Here is a sample run:
% zap top
force quit: 38555 ttys039 0:00.79 top? [y/n]y
terminating process 38555
force quit: 38556 ttys046 0:00.01 ./a.out top? [y/n]n
Note that the changes from the original code include the use of strstr()
replacing K&P’s function strindex()
, and the replacement of K&P’s function ttyin()
with a simple scanf()
. There was no strstr()
function in the standard string library at the time K&P wrote the book.
This works on OSX, and may have to be modified for other Unix variants.