--- Makefile +++ Makefile 2008-12-05 16:27:17.683465096 +0100 @@ -54,6 +54,8 @@ UBINPRG = ifeq ($(DISTRO),SuSE) UBINPRG += usleep UBINPRG += fsync + SBINPRG += vhangup + SBINPRG += mkill endif all: $(SBINPRG) $(UBINPRG) @@ -77,7 +79,7 @@ fsync: fsync.c $(CC) $(CFLAGS) -o $@ $^ clean: - $(RM) *.o *~ killproc startproc checkproc pidofproc start_daemon usleep fsync + $(RM) *.o *~ $(SBINPRG) $(UBINPRG) install: $(TODO) if test -n "$(SBINPRG)" ; then \ @@ -120,8 +122,12 @@ FILES = README \ libinit.h \ usleep.c \ usleep.1 \ - fsync.c \ - fsync.1 \ + fsync.c \ + fsync.1 \ + vhangup.c \ + vhangup.8 \ + mkill.c \ + mkill.8 \ killproc-$(VERSION).lsm dest: --- mkill.8 +++ mkill.8 2008-12-05 17:16:14.313863273 +0100 @@ -0,0 +1,77 @@ +.\" +.\" Copyright 2008 Werner Fink, 2008 SUSE LINUX Products GmbH, Germany. +.\" +.\" This program is free software; you can redistribute it and/or modify +.\" it under the terms of the GNU General Public License as published by +.\" the Free Software Foundation; either version 2 of the License, or +.\" (at your option) any later version. +.\" +.TH MKILL 8 "Jan 31, 2008" "Version 1.16" "The SuSE boot concept" +.UC 8 +.SH MKILL +Mkill \- Send processes making a active mount point busy a signal +.\" +.SH SYNOPSIS +.\" +.B mkill +[\-\fISIG\fR] +\fI/mnt1\fR [\fI/mnt2\fR...] +.\" +.SH DESCRIPTION +.B mkill +determines all active mount points from +.I /proc/mounts +and compares this with the specified mount points. Then +.B mkill +seeks for processes making this mount points busy. For +this search only the links found in +.I /proc// +are used to avoid hangs on files provided by network +file systems like +.BR nfs (5). +The default signal is +.B SIGTERM +for termination. If a mount point is not active, that is +that it is not found in +.IR /proc/mounts , +.B mkill +will do exactly nothing. +.\" +.SH OPTIONS +.TP +.B \-\fI\fP +Signals can be specified either by name +.RB (e.g. " -HUP" , " -SIGHUP" ) +or by number +.RB (e.g. " -1" ). +.IP +\." +.SH EXAMPLES +.nf +.B mkill -TERM /var + +.fi +This will terminate all processes accessing a seperate +.I /var +partition. +.PP +.nf +.B mkill -HUP /dev/pts + +.fi +All processes using a pseudo-terminal slave will +hangup. +\." +.SH RETURN VALUE +Always success which is that zero is returned. +\." +.SH SEE ALSO +.BR fuser (1), +.BR proc (5), +.BR umount (8). +\." +.SH COPYRIGHT +2008 Werner Fink, +2008 SUSE LINUX Products GmbH, Germany. +.SH AUTHOR +Werner Fink --- mkill.c +++ mkill.c 2008-12-05 17:09:02.275026090 +0100 @@ -0,0 +1,617 @@ +/* + * mkill.c Send a signal to all processes accessing a mount point + * + * Usage: mkill [-SIG] /mnt ... + * + * Copyright 2008 Werner Fink, 2008 SUSE LINUX Products GmbH, Germany. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Author: Werner Fink + */ + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) +# ifndef restrict +# define restrict __restrict__ +# endif +#endif +#define alignof(type) (sizeof(type)+(sizeof(type)%sizeof(void*))) + +typedef enum _boolean {false, true} boolean; +typedef struct _s_mnt +{ + struct _s_mnt *next; /* Pointer to next struct. */ + struct _s_mnt *prev; /* Pointer to previous st. */ + size_t nlen; + char * name; +} mntent_t; + +typedef struct _s_proc_ +{ + struct _s_proc_ *next; /* Pointer to next struct. */ + struct _s_proc_ *prev; /* Pointer to previous st. */ + pid_t pid; /* Process ID. */ +} proc_t; + +static mntent_t * mntent; +static proc_t * procs; +static char *sys_signame [NSIG+1]; +static char *sys_sigalias[NSIG+1]; + +static void init_signames(void); +static int signame_to_signum(const char *sig); +static void init_mnt(int argc, char* argv[]); +static void add_proc(pid_t pid); +static boolean check(const char *restrict name); + +int main(int argc, char* argv[]) +{ + const pid_t pid = getpid(); + proc_t * this, *ptr, * last; + struct dirent * dent; + int num, nsig = SIGTERM; + DIR * proc; + + num = argc; + while (--num) { + if (*(argv[num]) == '-') { + char *sig = argv[num]; + int tmp, len = strlen(sig); + sig++; + if ((tmp = atoi(sig)) > 0 && tmp < NSIG) { + memset(sig, '\0', len); + nsig = tmp; + break; + } else if ((tmp = signame_to_signum(sig)) > 0) { + memset(sig, '\0', len); + nsig = tmp; + break; + } + } + } + + for (num = 1; num < argc; num++) { + const size_t alen = strlen(argv[num]); + char * astr = argv[num]; + + if (alen == 0) + continue; + + if (*(astr+(alen-1)) == '/') + *(astr+(alen-1)) = '\0'; + } + + init_mnt(argc, argv); + + if ((proc = opendir("/proc")) == (DIR*)0) { + fprintf(stderr, "mkill: can not open /proc: %s\n", strerror(errno)); + exit(1); + } + + while ((dent = readdir(proc)) != (struct dirent*)0) { + const pid_t curr = (pid_t)atol(dent->d_name); + char path[256] = { "/proc/" }; + char name[PATH_MAX+1]; + char line[BUFSIZ+1]; + char * slash = &path[6]; + boolean found = false; + struct dirent * dfd; + ssize_t len; + size_t flen; + FILE * maps; + DIR * fdir; + + if (*dent->d_name == '.') + continue; + if (*dent->d_name < '0' || *dent->d_name > '9') + continue; + + if (pid == curr) + continue; + + *slash = '\0'; + strncpy(slash, dent->d_name, 246); + strcat(slash, "/root"); + + errno = 0; + if ((len = readlink(path, name, PATH_MAX)) < 0) + continue; + name[len] = '\0'; + if (check(name)) { + add_proc((pid_t)atol(dent->d_name)); + continue; + } + + *slash = '\0'; + strncpy(slash, dent->d_name, 246); + strcat(slash, "/cwd"); + + errno = 0; + if ((len = readlink(path, name, PATH_MAX)) < 0) + continue; + name[len] = '\0'; + if (check(name)) { + add_proc((pid_t)atol(dent->d_name)); + continue; + } + + *slash = '\0'; + strncpy(slash, dent->d_name, 246); + strcat(slash, "/exe"); + + errno = 0; + if ((len = readlink(path, name, PATH_MAX)) < 0) + continue; + name[len] = '\0'; + if (check(name)) { + add_proc((pid_t)atol(dent->d_name)); + continue; + } + + *slash = '\0'; + strncpy(slash, dent->d_name, 246); + strcat(slash, "/maps"); + + if ((maps = fopen(path, "r")) == (FILE*)0) + continue; + while (fgets(line, BUFSIZ, maps)) { + if (sscanf(line, "%*s %*s %*s %*x:%*x %*d %s", name) == 1) { + + if (name[0] == '\0' || name[0] == '[') + continue; + + if (check(name)) { + found = true; + break; + } + } + if (found) break; + } + (void)fclose(maps); + + if (found) { + add_proc((pid_t)atol(dent->d_name)); + continue; + } + + *(slash+strlen(dent->d_name)) = '\0'; + strcat(slash, "/fd"); + + if ((fdir = opendir(path)) == (DIR*)0) + continue; + strcat(slash, "/"); + flen = strlen(path); + while ((dfd = readdir(fdir)) != (struct dirent*)0) { + + if (*dfd->d_name == '.') + continue; + + slash = &path[flen]; + *slash = '\0'; + strcat(slash, dfd->d_name); + + errno = 0; + if ((len = readlink(path, name, PATH_MAX)) < 0) + continue; + name[len] = '\0'; + + if (check(name)) { + found = true; + break; + } + } + (void)closedir(fdir); + + if (found) + add_proc((pid_t)atol(dent->d_name)); + } + (void)closedir(proc); + + num = 0; + this = procs; + last = (proc_t*)0; + for(ptr = procs; this; ptr = this) { + last = ptr->prev; + this = ptr->next; + if (nsig) + kill(ptr->pid, nsig); + else { + if (num++ > 0) + putc(' ', stdout); + printf("%d", ptr->pid); + } + } + if (num > 0) + putc('\n', stdout); + return 0; +} + +static void init_mnt(int argc, char* argv[]) +{ + struct stat st; + struct mntent * ent; + FILE * mnt; + + /* Stat /proc/version to see if /proc is mounted. */ + if (stat("/proc/version", &st) < 0) { + fprintf(stderr, "mkill: /proc file system: %s\n", strerror(errno)); + exit(1); + } + + if ((mnt = setmntent("/proc/mounts", "r")) == (FILE*)0) { + fprintf(stderr, "mkill: can not open /proc/mounts: %s\n", strerror(errno)); + exit(1); + } + + while ((ent = getmntent(mnt))) { + const size_t nlen = strlen(ent->mnt_dir); + mntent_t *restrict ptr; + boolean found = false; + int num; + + for (num = 1; num < argc; num++) { + if (*(argv[num]) == '\0') + continue; + if ((found = (strcmp(argv[num], ent->mnt_dir) == 0))) + break; + } + + if (!found) + continue; + + if (posix_memalign((void*)&ptr, sizeof(void*), alignof(mntent_t)+(nlen+1)) != 0) { + fprintf(stderr, "mkill: %s\n", strerror(errno)); + exit(1); + } + ptr->name = ((char*)ptr)+alignof(mntent_t); + + strcpy(ptr->name, ent->mnt_dir); + ptr->nlen = strlen(ptr->name); + if (mntent) + mntent->prev = ptr; + ptr->next = mntent; + ptr->prev = (mntent_t*)0; + mntent = ptr; + } + + endmntent(mnt); +} + +static void add_proc(pid_t pid) +{ + proc_t * ptr = (proc_t*)malloc(sizeof(proc_t)); + ptr->pid = pid; + if (procs) + procs->prev = ptr; + ptr->next = procs; + ptr->prev = (proc_t*)0; + procs = ptr; +} + +static boolean check(const char *restrict name) +{ + mntent_t *p, *n, *l; + + n = mntent; + l = (mntent_t*)0; + for (p = mntent; n; p = n) { + l = p->prev; + n = p->next; + if (strncmp(name, p->name, p->nlen) == 0) + return true; + } + return false; +} + +static int signame_to_signum(const char *sig) +{ + int n; + + init_signames(); + if (!strncasecmp(sig, "sig", 3)) + sig += 3; + for (n = 1; n < NSIG+1; n++) { + if (sys_signame [n] && !strcasecmp(sys_signame [n], sig) ) + return n; + if (sys_sigalias[n] && !strcasecmp(sys_sigalias[n], sig) ) + return n; + } + return -1; +} + +static void init_signames(void) +{ + int n; + + for (n = 0; n < NSIG+1; n++) { + sys_signame [n] = (char *)0; + sys_sigalias[n] = (char *)0; + } + + sys_signame [0] = "EXIT"; + sys_sigalias[0] = "EXIT"; + +/* Signal Value Action Comment */ +#ifdef SIGHUP /* 1 A Hangup detected on controlling terminal or */ + /* death of controlling process */ + if (!sys_signame[SIGHUP]) + sys_signame [SIGHUP] = "HUP"; + else + sys_sigalias[SIGHUP] = "HUP"; +#endif +#ifdef SIGINT /* 2 A Interrupt from keyboard */ + + if (!sys_signame[SIGINT]) + sys_signame [SIGINT] = "INT"; + else + sys_sigalias[SIGINT] = "INT"; +#endif +#ifdef SIGQUIT /* 3 A Quit from keyboard */ + + if (!sys_signame[SIGQUIT]) + sys_signame [SIGQUIT] = "QUIT"; + else + sys_sigalias[SIGQUIT] = "QUIT"; +#endif +#ifdef SIGILL /* 4 A Illegal Instruction */ + + if (!sys_signame[SIGILL]) + sys_signame [SIGILL] = "ILL"; + else + sys_sigalias[SIGILL] = "ILL"; +#endif +#ifdef SIGABRT /* 6 C Abort signal from abort(3) */ + + if (!sys_signame[SIGABRT]) + sys_signame [SIGABRT] = "ABRT"; + else + sys_sigalias[SIGABRT] = "ABRT"; +#endif +#ifdef SIGFPE /* 8 C Floating point exception */ + if (!sys_signame[SIGFPE]) + sys_signame [SIGFPE] = "FPE"; + else + sys_sigalias[SIGFPE] = "FPE"; +#endif +#ifdef SIGKILL /* 9 AEF Kill signal */ + + if (!sys_signame[SIGKILL]) + sys_signame [SIGKILL] = "KILL"; + else + sys_sigalias[SIGKILL] = "KILL"; +#endif +#ifdef SIGSEGV /* 11 C Invalid memory reference */ + + if (!sys_signame[SIGSEGV]) + sys_signame [SIGSEGV] = "SEGV"; + else + sys_sigalias[SIGSEGV] = "SEGV"; +#endif +#ifdef SIGPIPE /* 13 A Broken pipe: */ + + if (!sys_signame[SIGPIPE]) + sys_signame [SIGPIPE] = "PIPE";/* write to pipe with no readers */ + else + sys_sigalias[SIGPIPE] = "PIPE";/* write to pipe with no readers */ +#endif +#ifdef SIGALRM /* 14 A Timer signal from alarm(1) */ + + if (!sys_signame[SIGALRM]) + sys_signame [SIGALRM] = "ALRM"; + else + sys_sigalias[SIGALRM] = "ALRM"; +#endif +#ifdef SIGTERM /* 15 A Termination signal */ + + if (!sys_signame[SIGTERM]) + sys_signame [SIGTERM] = "TERM"; + else + sys_sigalias[SIGTERM] = "TERM"; +#endif +#ifdef SIGUSR1 /* 30,10,16 A User-defined signal 1 */ + + if (!sys_signame[SIGUSR1]) + sys_signame [SIGUSR1] = "USR1"; + else + sys_sigalias[SIGUSR1] = "USR1"; +#endif +#ifdef SIGUSR2 /* 31,12,17 A User-defined signal 2 */ + + if (!sys_signame[SIGUSR2]) + sys_signame [SIGUSR2] = "USR2"; + else + sys_sigalias[SIGUSR2] = "USR2"; +#endif +#ifdef SIGCHLD /* 20,17,18 B Child stopped or terminated */ + + if (!sys_signame[SIGCHLD]) + sys_signame [SIGCHLD] = "CHLD"; + else + sys_sigalias[SIGCHLD] = "CHLD"; +#endif +#ifdef SIGCONT /* 19,18,25 Continue if stopped */ + + if (!sys_signame[SIGCONT]) + sys_signame [SIGCONT] = "CONT"; + else + sys_sigalias[SIGCONT] = "CONT"; +#endif +#ifdef SIGSTOP /* 17,19,23 DEF Stop process */ + + if (!sys_signame[SIGSTOP]) + sys_signame [SIGSTOP] = "STOP"; + else + sys_sigalias[SIGSTOP] = "STOP"; +#endif +#ifdef SIGTSTP /* 18,20,24 D Stop typed at tty */ + + if (!sys_signame[SIGTSTP]) + sys_signame [SIGTSTP] = "TSTP"; + else + sys_sigalias[SIGTSTP] = "TSTP"; +#endif +#ifdef SIGTTIN /* 21,21,26 D tty input for background process */ + + if (!sys_signame[SIGTTIN]) + sys_signame [SIGTTIN] = "TTIN"; + else + sys_sigalias[SIGTTIN] = "TTIN"; +#endif +#ifdef SIGTTOU /* 22,22,27 D tty output for background process */ + + if (!sys_signame[SIGTTOU]) + sys_signame [SIGTTOU] = "TTOU"; + else + sys_sigalias[SIGTTOU] = "TTOU"; +#endif +#ifdef SIGTRAP /* 5 CG Trace/breakpoint trap */ + + if (!sys_signame[SIGTRAP]) + sys_signame [SIGTRAP] = "TRAP"; + else + sys_sigalias[SIGTRAP] = "TRAP"; +#endif +#ifdef SIGIOT /* 6 CG IOT trap. A synonym for SIGABRT */ + + if (!sys_signame[SIGIOT]) + sys_signame [SIGIOT] = "IOT"; + else + sys_sigalias[SIGIOT] = "IOT"; +#endif +#ifdef SIGEMT /* 7,-,7 G */ + + if (!sys_signame[SIGEMT]) + sys_signame [SIGEMT] = "EMT"; + else + sys_sigalias[SIGEMT] = "EMT"; +#endif +#ifdef SIGBUS /* 10,7,10 AG Bus error */ + + if (!sys_signame[SIGBUS]) + sys_signame [SIGBUS] = "BUS"; + else + sys_sigalias[SIGBUS] = "BUS"; +#endif +#ifdef SIGSYS /* 12,-,12 G Bad argument to routine (SVID) */ + + if (!sys_signame[SIGSYS]) + sys_signame [SIGSYS] = "SYS"; + else + sys_sigalias[SIGSYS] = "SYS"; +#endif +#ifdef SIGSTKFLT /* -,16,- AG Stack fault on coprocessor */ + + if (!sys_signame[SIGSTKFLT]) + sys_signame [SIGSTKFLT] = "STKFLT"; + else + sys_sigalias[SIGSTKFLT] = "STKFLT"; +#endif +#ifdef SIGURG /* 16,23,21 BG Urgent condition on socket (4.2 BSD) */ + + if (!sys_signame[SIGURG]) + sys_signame [SIGURG] = "URG"; + else + sys_sigalias[SIGURG] = "URG"; +#endif +#ifdef SIGIO /* 23,29,22 AG I/O now possible (4.2 BSD) */ + + if (!sys_signame[SIGIO]) + sys_signame [SIGIO] = "IO"; + else + sys_sigalias[SIGIO] = "IO"; +#endif +#ifdef SIGPOLL /* AG A synonym for SIGIO (System V) */ + + if (!sys_signame[SIGPOLL]) + sys_signame [SIGPOLL] = "POLL"; + else + sys_sigalias[SIGPOLL] = "POLL"; +#endif +#ifdef SIGCLD /* -,-,18 G A synonym for SIGCHLD */ + + if (!sys_signame[SIGCLD]) + sys_signame [SIGCLD] = "CLD"; + else + sys_sigalias[SIGCLD] = "CLD"; +#endif +#ifdef SIGXCPU /* 24,24,30 AG CPU time limit exceeded (4.2 BSD) */ + + if (!sys_signame[SIGXCPU]) + sys_signame [SIGXCPU] = "XCPU"; + else + sys_sigalias[SIGXCPU] = "XCPU"; +#endif +#ifdef SIGXFSZ /* 25,25,31 AG File size limit exceeded (4.2 BSD) */ + + if (!sys_signame[SIGXFSZ]) + sys_signame [SIGXFSZ] = "XFSZ"; + else + sys_sigalias[SIGXFSZ] = "XFSZ"; +#endif +#ifdef SIGVTALRM /* 26,26,28 AG Virtual alarm clock (4.2 BSD) */ + + if (!sys_signame[SIGVTALRM]) + sys_signame [SIGVTALRM] = "VTALRM"; + else + sys_sigalias[SIGVTALRM] = "VTALRM"; +#endif +#ifdef SIGPROF /* 27,27,29 AG Profile alarm clock */ + + if (!sys_signame[SIGPROF]) + sys_signame [SIGPROF] = "PROF"; + else + sys_sigalias[SIGPROF] = "PROF"; +#endif +#ifdef SIGPWR /* 29,30,19 AG Power failure (System V) */ + + if (!sys_signame[SIGPWR]) + sys_signame [SIGPWR] = "PWR"; + else + sys_sigalias[SIGPWR] = "PWR"; +#endif +#ifdef SIGINFO /* 29,-,- G A synonym for SIGPWR */ + + if (!sys_signame[SIGINFO]) + sys_signame [SIGINFO] = "INFO"; + else + sys_sigalias[SIGINFO] = "INFO"; +#endif +#ifdef SIGLOST /* -,-,- AG File lock lost */ + + if (!sys_signame[SIGLOST]) + sys_signame [SIGLOST] = "LOST"; + else + sys_sigalias[SIGLOST] = "LOST"; +#endif +#ifdef SIGWINCH /* 28,28,20 BG Window resize signal (4.3 BSD, Sun) */ + + if (!sys_signame[SIGWINCH]) + sys_signame [SIGWINCH] = "WINCH"; + else + sys_sigalias[SIGWINCH] = "WINCH"; +#endif +#ifdef SIGUNUSED /* -,31,- AG Unused signal */ + + if (!sys_signame[SIGUNUSED]) + sys_signame [SIGUNUSED] = "UNUSED"; + else + sys_sigalias[SIGUNUSED] = "UNUSED"; +#endif +} --- vhangup.8 +++ vhangup.8 2008-12-05 16:32:40.062425538 +0100 @@ -0,0 +1,47 @@ +.\" +.\" Copyright 2008 Werner Fink, 2008 SUSE LINUX Products GmbH, Germany. +.\" +.\" This program is free software; you can redistribute it and/or modify +.\" it under the terms of the GNU General Public License as published by +.\" the Free Software Foundation; either version 2 of the License, or +.\" (at your option) any later version. +.\" +.TH VHANGUP 8 "Jan 31, 2008" "Version 1.16" "The SuSE boot concept" +.UC 8 +.SH NAME +Vhangup \- Cause a virtually hangup on the specified terminals +.\" +.SH SYNOPSIS +.\" +.B vhangup +[\fI/dev/\fR [\fI/dev/\fR]] +.\" +.SH DESCRIPTION +.B vhangup +simulates a hangup on the specified terminals. Not existing +device files or devices will be ignored. +\." +.SH EXAMPLES +.nf +.B vhangup /dev/tty1 /dev/tty2 /dev/tty3 /dev/tty4 /dev/tty5 /dev/tty6 /dev/ttyS1 + +.fi +This will replace all open file descriptors in the kernel that points +to the listed ttys by a dummy that will deny further reading/writing +to the device. It also send the signals SIGHUP/SIGCONT to the processes +which have file descriptors open on the listed ttys. +\." +.SH RETURN VALUE +On success, zero is returned. On error, 1 is returned. +\." +.SH SEE ALSO +.BR vhangup (2), +.BR tty (4), +.BR ttyS (4), +.BR pts (4). +\." +.SH COPYRIGHT +2008 Werner Fink, +2008 SUSE LINUX Products GmbH, Germany. +.SH AUTHOR +Werner Fink --- vhangup.c +++ vhangup.c 2008-12-05 11:47:35.881479139 +0100 @@ -0,0 +1,77 @@ +/* + * vhangup.c Cause a hangup on the specified terminals + * + * Usage: vhangup /dev/tty1 ... + * + * Copyright 2008 Werner Fink, 2008 SUSE LINUX Products GmbH, Germany. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Author: Werner Fink + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char* argv[]) +{ + int ret; + + switch (fork()) { + case -1: + fprintf(stderr, "vhangup: %s\n", strerror(errno)); + return 1; + case 0: { + struct sigaction sa, sa_old; + int num; + + setsid(); + + sa.sa_flags = 0; + sa.sa_handler = SIG_IGN; + sigemptyset (&sa.sa_mask); + sigaction (SIGHUP, &sa, &sa_old); + + for (ret = num = 1; num < argc; num++) { + int fd = open(argv[num], O_RDWR|O_NONBLOCK|O_NOCTTY, 0); + if (fd < 0) { + switch (errno) { + case ENOENT: + case ENODEV: + case ENXIO: + ret++; + default: + break; + } + continue; + } + if ((ioctl (fd, TIOCSCTTY, 1) == 0) && (vhangup() == 0)) + ret++; + close(fd); + } + + sigaction (SIGHUP, &sa_old, NULL); + exit(ret != num); + } + default: + waitpid(-1, &ret, 0); + break; + } + + return (WIFEXITED(ret)) ? WEXITSTATUS(ret) : 1; +}