diff options
Diffstat (limited to 'utempter.c')
-rw-r--r-- | utempter.c | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/utempter.c b/utempter.c new file mode 100644 index 0000000..dd65520 --- /dev/null +++ b/utempter.c @@ -0,0 +1,161 @@ +#define _GNU_SOURCE 1 + +#include <errno.h> +#include <fcntl.h> +#include <pty.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/sysmacros.h> +#include <utmp.h> +#include <utmpx.h> +#include <unistd.h> + +static void usage(void) { + fprintf(stderr, "Usage: -a <slave-device-name> <host>\n" + " -d <slave-device-name>\n"); + exit(1); +} + +static void checkDevice(const char * device) { + struct stat sb1, sb2; + int flags; + + if (strstr(device, "/../") || strstr(device, "/./") || + strstr(device, "//")) { + fprintf(stderr, "a simple path is required\n"); + exit(1); + } + + if (strncmp(device, "/dev/", 5)) { + fprintf(stderr, "the device must begin with /dev/\n"); + exit(1); + } + + if (fstat(3, &sb1)) { + fprintf(stderr, "error stat'ing fd 3: %s\n", strerror(errno)); + exit(1); + } + + if (major(sb1.st_rdev) == 2) { + if (lstat(device, &sb2)) { + fprintf(stderr, "error stat'ing %s: %s\n", device, strerror(errno)); + exit(1); + } + + if (major(sb2.st_rdev) != 3) { + fprintf(stderr, "%s is not the slave for the master pty on fd 3\n", + device); + exit(1); + } + + if (minor(sb1.st_rdev) != minor(sb2.st_rdev)) { + fprintf(stderr, "fd 3 and %s are not the same device\n", + device); + exit(1); + } + } else if (major(sb1.st_rdev) == 5 && minor(sb1.st_rdev) == 2) { + char * dev2 = ptsname(3); + + if (!dev2 || strcmp(dev2, device)) { + fprintf(stderr, "passed master doesn't match slave name\n"); + exit(1); + } + } else { + fprintf(stderr, "fd 3 is not a master pty device (it is %d, %d)\n", + major(sb1.st_rdev), minor(sb1.st_rdev)); + exit(1); + } + + if ((flags = fcntl(3, F_GETFL, 0)) < 0) { + fprintf(stderr, "error getting flags for fd 3: %s\n", strerror(errno)); + exit(1); + } + + if ((flags & O_RDWR) != O_RDWR) { + fprintf(stderr, "fd 3 is not read/writeable\n"); + exit(1); + } +} + +int main(int argc, const char ** argv) { + struct utmpx utx; + int add; + const char * device, * host; + struct passwd * pw; + int i; + struct stat sb; + char * id; + + if (argc < 3) usage(); + + for (i = 0; i < 3; i++) { + if (fstat(i, &sb)) { + fprintf(stderr, "error: failed to stat %d\n", i); + exit(1); + } + } + + if (!strcmp(argv[1], "-a")) { + if (argc > 4) usage(); + add = 1; + } else if (!strcmp(argv[1], "-d")) { + if (argc != 3) usage(); + add = 0; + } else + usage(); + + device = argv[2]; + + if (!getuid()) { + host = argv[3]; /* either NULL or something real */ + } else { + host = NULL; + } + + memset(&utx, 0, sizeof(utx)); + if (add) + utx.ut_type = USER_PROCESS; + else + utx.ut_type = DEAD_PROCESS; + + utx.ut_pid = getppid(); + if (utx.ut_pid == 1) { + fprintf(stderr, "error: parent pid should not be init\n"); + exit(1); + } + + checkDevice(device); + + strncpy(utx.ut_line, device + 5, sizeof(utx.ut_line)); + + pw = getpwuid(getuid()); + if (!pw) { + fprintf(stderr, "cannot find user w/ uid %d\n", getuid()); + exit(1); + } + + strncpy(utx.ut_user, pw->pw_name, sizeof(utx.ut_user)); + + if (host) + strncpy(utx.ut_host, host, sizeof(utx.ut_host)); + + if (!strncmp("pts/", utx.ut_line, 4)) { + id = utx.ut_line + 3; + if (strlen(id) > 4) id++; + } else { + id = utx.ut_line + 2; + } + + strncpy(utx.ut_id, id, sizeof(utx.ut_id)); + + gettimeofday(&utx.ut_tv, NULL); + + pututxline(&utx); + updwtmpx(_PATH_WTMP, &utx); + + return 0; +} |