sysvinit/killproc-2.13.dif

866 lines
20 KiB
Plaintext

--- 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/<pid>/
+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<SIG>\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 <werner@suse.de>
--- 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 <werner@suse.de>
+ */
+
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+#include <dirent.h>
+#include <errno.h>
+#include <mntent.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#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/<terminal>\fR [\fI/dev/<terminal>\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 <werner@suse.de>
--- 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 <werner@suse.de>
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <unistd.h>
+
+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;
+}