diff options
-rw-r--r-- | TODO | 2 | ||||
-rw-r--r-- | configure.ac | 8 | ||||
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/ck-sysdeps-freebsd.c | 592 |
4 files changed, 182 insertions, 421 deletions
@@ -20,5 +20,3 @@ TODO - Figure out how to register activation handlers - Use a configuration file for defining how to add sessions to seats - - - Write native FreeBSD ck-sysdeps-freebsd.c. diff --git a/configure.ac b/configure.ac index d52a56d..ab2db1c 100644 --- a/configure.ac +++ b/configure.ac @@ -140,9 +140,15 @@ dnl Figure out what tools backend to build dnl --------------------------------------------------------------------------- CK_BACKEND="" +KVM_LIBS="" case "$host" in *-*-freebsd*) CK_BACKEND="freebsd" + AC_CHECK_LIB(kvm, kvm_openfiles, have_kvm=yes, + AC_MSG_ERROR([Unable to find libkvm which is needed on FreeBSD])) + if test "x$have_kvm" = "xyes"; then + KVM_LIBS="-lkvm" + fi ;; *-*-linux*) CK_BACKEND="linux" @@ -152,6 +158,8 @@ case "$host" in ;; esac +AC_SUBST(KVM_LIBS) + AM_CONDITIONAL(CK_COMPILE_LINUX, test x$CK_BACKEND = xlinux, [Compiling for Linux]) AM_CONDITIONAL(CK_COMPILE_FREEBSD, test x$CK_BACKEND = xfreebsd, [Compiling for FreeBSD]) AM_CONDITIONAL(CK_COMPILE_SOLARIS, test x$CK_BACKEND = xsolaris, [Compiling for Solaris]) diff --git a/src/Makefile.am b/src/Makefile.am index eee798d..941a41d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -46,6 +46,7 @@ if CK_COMPILE_FREEBSD libck_la_SOURCES += \ ck-sysdeps-freebsd.c \ $(NULL) +libck_la_LIBADD = $(KVM_LIBS) endif EXTRA_libck_la_SOURCES = \ diff --git a/src/ck-sysdeps-freebsd.c b/src/ck-sysdeps-freebsd.c index 729af2f..68e8c9d 100644 --- a/src/ck-sysdeps-freebsd.c +++ b/src/ck-sysdeps-freebsd.c @@ -1,5 +1,6 @@ -/* - * Copyright (C) 2007 Florent Thoumie <flz@xbsd.org> +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 Joe Marcus Clarke <marcus@FreeBSD.org> * * 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 @@ -24,12 +25,19 @@ #include <fcntl.h> #include <unistd.h> #include <string.h> +#include <errno.h> +#include <paths.h> +#include <ttyent.h> +#include <kvm.h> +#include <sys/param.h> #include <sys/types.h> #include <sys/stat.h> +#include <sys/sysctl.h> +#include <sys/user.h> -#ifdef HAVE_PATHS_H -#include <paths.h> -#endif /* HAVE_PATHS_H */ +#define DEV_ENCODE(M,m) ( \ + ( (M&0xfff) << 8) | ( (m&0xfff00) << 12) | (m&0xff) \ +) #include "ck-sysdeps.h" @@ -71,198 +79,10 @@ struct _CkProcessStat int tpgid; /* stat terminal process group id */ int exit_signal; /* stat might not be SIGCHLD */ int processor; /* stat current (or most recent?) CPU */ -}; - -/* adapted from procps */ -#define MAJOR_OF(d) ( ((unsigned)(d)>>8u) & 0xfffu ) -#define MINOR_OF(d) ( ((unsigned)(d)&0xffu) | (((unsigned)(d)&0xfff00000u)>>12u) ) - -typedef struct tty_map_node { - struct tty_map_node *next; - guint major_number; - guint minor_first; - guint minor_last; - char name[16]; - char devfs_type; -} tty_map_node; - -static tty_map_node *tty_map = NULL; - -#if 0 -/* adapted from procps */ -/* Load /proc/tty/drivers for device name mapping use. */ -static void -load_drivers (void) -{ - char buf[10000]; - char *p; - int fd; - int bytes; - - fd = open ("/proc/tty/drivers", O_RDONLY); - if (fd == -1) { - goto fail; - } - - bytes = read (fd, buf, sizeof (buf) - 1); - if (bytes == -1) { - goto fail; - } - - buf[bytes] = '\0'; - p = buf; - while ((p = strstr (p, " " _PATH_DEV))) { - tty_map_node *tmn; - int len; - char *end; - - p += 6; - end = strchr (p, ' '); - if (! end) { - continue; - } - len = end - p; - tmn = calloc (1, sizeof (tty_map_node)); - tmn->next = tty_map; - tty_map = tmn; - /* if we have a devfs type name such as /dev/tts/%d then strip the %d but - keep a flag. */ - if (len >= 3 && !strncmp (end - 2, "%d", 2)) { - len -= 2; - tmn->devfs_type = 1; - } - strncpy (tmn->name, p, len); - p = end; /* set p to point past the %d as well if there is one */ - while (*p == ' ') { - p++; - } - - tmn->major_number = atoi (p); - p += strspn (p, "0123456789"); - while (*p == ' ') { - p++; - } - switch (sscanf (p, "%u-%u", &tmn->minor_first, &tmn->minor_last)) { - default: - /* Can't finish parsing this line so we remove it from the list */ - tty_map = tty_map->next; - free (tmn); - break; - case 1: - tmn->minor_last = tmn->minor_first; - break; - case 2: - break; - } - } - fail: - if (fd != -1) { - close (fd); - } - if(! tty_map) { - tty_map = (tty_map_node *)-1; - } -} - -/* adapted from procps */ -/* Try to guess the device name from /proc/tty/drivers info. */ -static char * -driver_name (guint maj, - guint min) -{ - struct stat sbuf; - tty_map_node *tmn; - char *tty; - - if (! tty_map) { - load_drivers (); - } - if (tty_map == (tty_map_node *) - 1) { - return 0; - } - - tmn = tty_map; - for (;;) { - if (! tmn) { - return 0; - } - if (tmn->major_number == maj && tmn->minor_first <= min && tmn->minor_last >= min) { - break; - } - tmn = tmn->next; - } - - tty = g_strdup_printf (_PATH_DEV "%s%d", tmn->name, min); /* like "/dev/ttyZZ255" */ - if (stat (tty, &sbuf) < 0){ - g_free (tty); - - if (tmn->devfs_type) { - return NULL; - } + uintptr_t penv; /* stat address of initial environment vector */ + char tty_text[16]; /* stat device name */ - tty = g_strdup_printf (_PATH_DEV "%s", tmn->name); /* like "/dev/ttyZZ255" */ - - if (stat (tty, &sbuf) < 0) { - g_free (tty); - return NULL; - } - } - - if (min != MINOR_OF (sbuf.st_rdev)) { - g_free (tty); - return NULL; - } - - if (maj != MAJOR_OF (sbuf.st_rdev)) { - g_free (tty); - return NULL; - } - - return tty; -} -#endif - -/* adapted from procps */ -static char * -link_name (guint maj, - guint min, - int pid, - const char *name) -{ - struct stat sbuf; - char *path; - char *tty; - - /* XXX - Will have to switch to native procfs at some stage */ - path = g_strdup_printf ("/compat/linux/proc/%d/%s", pid, name); - tty = g_file_read_link (path, NULL); - g_free (path); - - if (tty == NULL) { - goto out; - } - - if (stat (tty, &sbuf) < 0) { - g_free (tty); - tty = NULL; - goto out; - } - - if (min != MINOR_OF (sbuf.st_rdev)) { - g_free (tty); - tty = NULL; - goto out; - - } - if (maj != MAJOR_OF (sbuf.st_rdev)) { - g_free (tty); - tty = NULL; - goto out; - } - - out: - return tty; -} +}; pid_t ck_process_stat_get_ppid (CkProcessStat *stat) @@ -292,105 +112,87 @@ ck_process_stat_get_tty (CkProcessStat *stat) g_return_val_if_fail (stat != NULL, NULL); - pid = stat->pid; - dev = stat->tty; + return g_strdup (stat->tty_text); +} - if (dev == 0u) { - return NULL; - } +static gboolean +get_kinfo_proc (pid_t pid, + struct kinfo_proc *p) +{ + int mib[4]; + size_t len; - dev_maj = MAJOR_OF (dev); - dev_min = MINOR_OF (dev); + len = 4; + sysctlnametomib ("kern.proc.pid", mib, &len); - tty = link_name (dev_maj, dev_min, pid, "tty"); - if (tty != NULL) { - goto out; - } -#if 0 - tty = driver_name (dev_maj, dev_min); - if (tty != NULL) { - goto out; - } -#endif - tty = link_name (dev_maj, dev_min, pid, "fd/2"); - if (tty != NULL) { - goto out; - } + len = sizeof(struct kinfo_proc); + mib[3] = pid; - tty = link_name (dev_maj, dev_min, pid, "fd/255"); - if (tty != NULL) { - goto out; - } + if (sysctl (mib, 4, p, &len, NULL, 0) == -1) { + return FALSE; + } - out: - - return tty; + return TRUE; } -#define KLF "l" -/* adapted from procps */ -static void -stat2proc (const char *S, +/* return 1 if it works, or 0 for failure */ +static gboolean +stat2proc (pid_t pid, CkProcessStat *P) { - unsigned num; - char * tmp; - - /* fill in default values for older kernels */ - P->processor = 0; - P->rtprio = -1; - P->sched = -1; - P->nlwp = 0; - - S = strchr (S, '(') + 1; - tmp = strrchr (S, ')'); - num = tmp - S; - if (G_UNLIKELY (num >= sizeof P->cmd)) { + struct kinfo_proc p; + char *ttname; + int num; + int tty_maj; + int tty_min; + + if (! get_kinfo_proc (pid, &p)) { + return FALSE; + } + + num = OCOMMLEN; + if (num >= sizeof P->cmd) { num = sizeof P->cmd - 1; } - memcpy (P->cmd, S, num); - P->cmd[num] = '\0'; - S = tmp + 2; /* skip ") " */ - - num = sscanf (S, - "%c " - "%d %d %d %d %d " - "%lu %lu %lu %lu %lu " - "%Lu %Lu %Lu %Lu " /* utime stime cutime cstime */ - "%ld %ld " - "%d " - "%ld " - "%Lu " /* start_time */ - "%lu " - "%ld " - "%lu %"KLF"u %"KLF"u %"KLF"u %"KLF"u %"KLF"u " - "%*s %*s %*s %*s " /* discard, no RT signals & Linux 2.1 used hex */ - "%"KLF"u %*lu %*lu " - "%d %d " - "%lu %lu", - &P->state, - &P->ppid, &P->pgrp, &P->session, &P->tty, &P->tpgid, - &P->flags, &P->min_flt, &P->cmin_flt, &P->maj_flt, &P->cmaj_flt, - &P->utime, &P->stime, &P->cutime, &P->cstime, - &P->priority, &P->nice, - &P->nlwp, - &P->alarm, - &P->start_time, - &P->vsize, - &P->rss, - &P->rss_rlim, &P->start_code, &P->end_code, &P->start_stack, &P->kstk_esp, &P->kstk_eip, - /* P->signal, P->blocked, P->sigignore, P->sigcatch, */ /* can't use */ - &P->wchan, /* &P->nswap, &P->cnswap, */ /* nswap and cnswap dead for 2.4.xx and up */ - /* -- Linux 2.0.35 ends here -- */ - &P->exit_signal, &P->processor, /* 2.2.1 ends with "exit_signal" */ - /* -- Linux 2.2.8 to 2.5.17 end here -- */ - &P->rtprio, &P->sched /* both added to 2.5.18 */ - ); - - if (!P->nlwp){ - P->nlwp = 1; + memcpy (P->cmd, p.ki_ocomm, num); + + P->cmd[num] = '\0'; + P->pid = p.ki_pid; + P->ppid = p.ki_ppid; + P->pgrp = p.ki_pgid; + P->session = p.ki_sid; + P->rss = p.ki_rssize; + P->vsize = p.ki_size; + P->start_time = p.ki_start.tv_sec; + P->wchan = p.ki_wchan; + P->state = p.ki_stat; + P->nice = p.ki_nice; + P->flags = p.ki_sflag; + P->tpgid = p.ki_tpgid; + P->processor = p.ki_oncpu; + P->nlwp = p.ki_numthreads; + + /* we like it Linux-encoded :-) */ + tty_maj = major (p.ki_tdev); + tty_min = minor (p.ki_tdev); + P->tty = DEV_ENCODE (tty_maj,tty_min); + + snprintf (P->tty_text, sizeof P->tty_text, "%3d,%-3d", tty_maj, tty_min); + + if (p.ki_tdev != NODEV && (ttname = devname (p.ki_tdev, S_IFCHR)) != NULL) { + memcpy (P->tty_text, ttname, 8); + } + + if (p.ki_tdev == NODEV) { + memcpy (P->tty_text, " ? ", 8); + } + + if (P->pid != pid) { + return FALSE; } + + return TRUE; } gboolean @@ -398,11 +200,11 @@ ck_process_stat_new_for_unix_pid (pid_t pid, CkProcessStat **stat, GError **error) { - char *path; - char *contents; - gsize length; - gboolean res; - GError *local_error; + char *path; + char *contents; + gsize length; + gboolean res; + GError *local_error; CkProcessStat *proc; g_return_val_if_fail (pid > 1, FALSE); @@ -411,28 +213,16 @@ ck_process_stat_new_for_unix_pid (pid_t pid, return FALSE; } - /* XXX - Will have to switch to native procfs at some stage */ - path = g_strdup_printf ("/compat/linux/proc/%d/stat", pid); - - contents = NULL; - local_error = NULL; - res = g_file_get_contents (path, - &contents, - &length, - &local_error); + proc = g_new0 (CkProcessStat, 1); + proc->pid = pid; + res = stat2proc (pid, proc); if (res) { - proc = g_new0 (CkProcessStat, 1); - proc->pid = pid; - stat2proc (contents, proc); *stat = proc; } else { g_propagate_error (error, local_error); *stat = NULL; } - g_free (contents); - g_free (path); - return res; } @@ -445,61 +235,44 @@ ck_process_stat_free (CkProcessStat *stat) GHashTable * ck_unix_pid_get_env_hash (pid_t pid) { - char *path; - gboolean res; - char *contents; - gsize length; - GError *error; - GHashTable *hash; - int i; - gboolean last_was_null; - - g_return_val_if_fail (pid > 1, NULL); - - contents = NULL; - hash = NULL; - - /* XXX - Will have to switch to native procfs at some stage */ - path = g_strdup_printf ("/compat/linux/proc/%u/environ", (guint)pid); - - error = NULL; - res = g_file_get_contents (path, - &contents, - &length, - &error); - if (! res) { - g_warning ("Couldn't read %s: %s", path, error->message); - g_error_free (error); - goto out; - } + GHashTable *hash; + char **penv; + kvm_t *kd; + struct kinfo_proc p; + int i; + + kd = kvm_openfiles (_PATH_DEVNULL, _PATH_DEVNULL, NULL, O_RDONLY, NULL); + if (kd == NULL) { + return NULL; + } + + if (! get_kinfo_proc (pid, &p)) { + return NULL; + } + + penv = kvm_getenvv (kd, &p, 0); + if (penv == NULL) { + return NULL; + } hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - last_was_null = TRUE; - for (i = 0; i < length; i++) { - if (contents[i] == '\0') { - last_was_null = TRUE; - continue; - } - if (last_was_null) { - char **vals; - vals = g_strsplit (contents + i, "=", 2); - if (vals != NULL) { - g_hash_table_insert (hash, - g_strdup (vals[0]), - g_strdup (vals[1])); - g_strfreev (vals); - } - } - last_was_null = FALSE; + for (i = 0; penv[i] != NULL; i++) { + char **vals; + + vals = g_strsplit (penv[i], "=", 2); + if (vals != NULL) { + g_hash_table_insert (hash, + g_strdup (vals[0]), + g_strdup (vals[1])); + g_strfreev (vals); + } } - out: - g_free (contents); - g_free (path); + kvm_close (kd); return hash; } @@ -508,89 +281,43 @@ char * ck_unix_pid_get_env (pid_t pid, const char *var) { - char *path; - gboolean res; - char *contents; - char *val; - gsize length; - GError *error; - int i; - char *prefix; - int prefix_len; - gboolean last_was_null; - - g_return_val_if_fail (pid > 1, NULL); - - val = NULL; - contents = NULL; - prefix = NULL; - - /* XXX - Will have to switch to native procfs at some stage */ - path = g_strdup_printf ("/compat/linux/proc/%u/environ", (guint)pid); - - error = NULL; - res = g_file_get_contents (path, - &contents, - &length, - &error); - if (! res) { - g_warning ("Couldn't read %s: %s", path, error->message); - g_error_free (error); - goto out; - } - - - prefix = g_strdup_printf ("%s=", var); - prefix_len = strlen (prefix); - - /* FIXME: make more robust */ - last_was_null = TRUE; - for (i = 0; i < length; i++) { - if (contents[i] == '\0') { - last_was_null = TRUE; - continue; - } - if (last_was_null && g_str_has_prefix (contents + i, prefix)) { - val = g_strdup (contents + i + prefix_len); - break; - } - last_was_null = FALSE; - } + GHashTable *hash; + char *val; - out: - g_free (prefix); - g_free (contents); - g_free (path); + /* + * Would probably be more efficient to just loop through the + * environment and return the value, avoiding building the hash + * table, but this works for now. + */ + hash = ck_unix_pid_get_env_hash (pid); + val = g_strdup (g_hash_table_lookup (hash, var)); + g_hash_table_destroy (hash); return val; } uid_t -ck_unix_pid_get_uid (pid_t pid) +proc_pid_get_uid (pid_t pid) { - struct stat st; - char *path; - int uid; - int res; + uid_t uid; + gboolean res; + struct kinfo_proc p; g_return_val_if_fail (pid > 1, 0); uid = -1; - /* XXX - Will have to switch to native procfs at some stage */ - path = g_strdup_printf ("/compat/linux/proc/%u", (guint)pid); - res = stat (path, &st); - g_free (path); + res = get_kinfo_proc (pid, &p); - if (res == 0) { - uid = st.st_uid; + if (res) { + uid = p.ki_uid; } return uid; } pid_t -ck_unix_pid_get_ppid (pid_t pid) +proc_pid_get_ppid (pid_t pid) { int ppid; gboolean res; @@ -616,11 +343,38 @@ ck_unix_pid_get_ppid (pid_t pid) gboolean ck_get_max_num_consoles (guint *num) { - if (num != NULL) { - *num = 0x0f; /* XXX - Eeeek! */ - } + int max_consoles; + int res; + gboolean ret; + struct ttyent *t; - return TRUE; + ret = FALSE; + max_consoles = 0; + + res = setttyent (); + if (res == 0) { + goto done; + } + + while ((t = getttyent ()) != NULL) { + if (t->ty_status & TTY_ON && strncmp (t->ty_name, "ttyv", 4) == 0) + max_consoles++; + } + + if (errno == 0) { + ret = TRUE; + } else { + max_consoles = 0; + } + + endttyent (); + +done: + if (num != NULL) { + *num = max_consoles; + } + + return ret; } char * @@ -628,7 +382,7 @@ ck_get_console_device_for_num (guint num) { char *device; - device = g_strdup_printf (_PATH_TTY "%u", num); + device = g_strdup_printf ("/dev/ttyv%u", num); return device; } @@ -647,7 +401,7 @@ ck_get_console_num_from_device (const char *device, return FALSE; } - if (sscanf (device, _PATH_TTY "%u", &n) == 1) { + if (sscanf (device, "/dev/ttyv%u", &n) == 1) { ret = TRUE; } |