/** * type_var_to_string: * @parent: parent object for new string, * @var: variable to convert. * * Returns a string for the given variable @var, consisting of the type * and variable name separated by a space if appropriate. * * If @parent is not NULL, it should be a pointer to another object which * will be used as a parent for the returned string. When all parents * of the returned string are freed, the returned string will also be * freed. * * Returns: the newly allocated string or NULL if insufficient memory. **/ char * type_var_to_string (const void *parent, TypeVar * var) { char *str; nih_assert (var != NULL); if (strchr (var->type, '*')) { str = nih_sprintf (parent, "%s%s", var->type, var->name); } else { str = nih_sprintf (parent, "%s %s", var->type, var->name); } if (! str) return NULL; if (var->array) { if (! nih_strcat (&str, parent, "[]")) { nih_free (str); return NULL; } } return str; }
void test_sprintf (void) { char *str1, *str2; TEST_FUNCTION ("nih_sprintf"); /* Check that we can create a formatted string with no parent, * it should be allocated with nih_alloc and be the right length. */ TEST_FEATURE ("with no parent"); TEST_ALLOC_FAIL { str1 = nih_sprintf (NULL, "this %s a test %d", "is", 54321); if (test_alloc_failed) { TEST_EQ_P (str1, NULL); continue; } TEST_ALLOC_PARENT (str1, NULL); TEST_ALLOC_SIZE (str1, strlen (str1) + 1); TEST_EQ_STR (str1, "this is a test 54321"); nih_free (str1); } /* Check that we can create a string with a parent. */ TEST_FEATURE ("with a parent"); str1 = nih_sprintf (NULL, "this %s a test %d", "is", 54321); TEST_ALLOC_FAIL { str2 = nih_sprintf (str1, "another %d test %s", 12345, "string"); if (test_alloc_failed) { TEST_EQ_P (str2, NULL); continue; } TEST_ALLOC_PARENT (str2, str1); TEST_ALLOC_SIZE (str2, strlen (str2) + 1); TEST_EQ_STR (str2, "another 12345 test string"); nih_free (str2); } nih_free (str1); }
/** * apparmor_switch: * @profile: AppArmor profile to switch to * * This function switches to a new AppArmor profile on exec * * Returns: zero on success, -1 on error **/ int apparmor_switch (char *profile) { nih_local char *filename = NULL; FILE *f; nih_assert (profile != NULL); /* Silently fail if AppArmor isn't enabled. */ if (! apparmor_available()) return 0; filename = nih_sprintf (NULL, "/proc/%d/attr/exec", getpid()); if (! filename) return -1; f = fopen (filename, "w"); if (! f) return -1; fprintf (f, "exec %s\n", profile); if (fclose (f)) return -1; return 0; }
int chown_main(const char *controller, const char *cgroup, struct ucred p, struct ucred r, struct ucred v) { char rcgpath[MAXPATHLEN]; nih_local char *path = NULL; uid_t uid; /* If caller is not root in his userns, then he can't chown, as * that requires privilege over two uids */ if (r.uid) { if (!hostuid_to_ns(r.uid, r.pid, &uid) || uid != 0) { nih_error("%s: Chown requested by non-root uid %u", __func__, r.uid); return -1; } } if (!sane_cgroup(cgroup)) { nih_error("%s: unsafe cgroup", __func__); return -1; } // Get r's current cgroup in rcgpath if (!compute_pid_cgroup(r.pid, controller, "", rcgpath, NULL)) { nih_error("%s: Could not determine the requested cgroup", __func__); return -1; } /* rcgpath + / + cgroup + \0 */ if (strlen(rcgpath) + strlen(cgroup) > MAXPATHLEN+2) { nih_error("%s: Path name too long", __func__); return -1; } path = NIH_MUST( nih_sprintf(NULL, "%s/%s", rcgpath, cgroup) ); if (realpath_escapes(path, rcgpath)) { nih_error("%s: Invalid path %s", __func__, path); return -1; } // is r allowed to descend under the parent dir? if (!may_access(r.pid, r.uid, r.gid, path, O_RDONLY)) { nih_error("%s: pid %d (uid %u gid %u) may not read under %s", __func__, r.pid, r.uid, r.gid, path); return -1; } // does r have privilege over the cgroup dir? if (!may_access(r.pid, r.uid, r.gid, path, O_RDWR)) { nih_error("%s: Pid %d may not chown %s\n", __func__, r.pid, path); return -1; } // go ahead and chown it. if (!chown_cgroup_path(path, v.uid, v.gid, false)) { nih_error("%s: Failed to change ownership on %s to %u:%u", __func__, path, v.uid, v.gid); return -1; } return 0; }
static void prune_user_cgs(const char *user) { nih_local char **list = NULL; nih_local char *path = NULL; int i; path = NIH_MUST( nih_sprintf(NULL, "user/%s", user) ); list = cgm_list_children(path); if (!list) return; for (i = 0; list[i]; i++) { nih_local char *cgpath = NIH_MUST( nih_sprintf(NULL, "%s/%s", path, list[i]) ); if (!cgm_cg_has_tasks(cgpath)) cgm_clear_cgroup(cgpath); } if (!cgm_cg_has_tasks(path)) cgm_clear_cgroup(path); }
int chmod_main(const char *controller, const char *cgroup, const char *file, struct ucred p, struct ucred r, int mode) { char rcgpath[MAXPATHLEN]; nih_local char *path = NULL; if (!sane_cgroup(cgroup)) { nih_error("%s: unsafe cgroup", __func__); return -1; } if (file && ( strchr(file, '/') || strchr(file, '\\')) ) { nih_dbus_error_raise_printf (DBUS_ERROR_INVALID_ARGS, "invalid file"); return -1; } // Get r's current cgroup in rcgpath if (!compute_pid_cgroup(r.pid, controller, "", rcgpath, NULL)) { nih_error("%s: Could not determine the requested cgroup", __func__); return -1; } path = NIH_MUST( nih_sprintf(NULL, "%s/%s", rcgpath, cgroup) ); if (file && strlen(file)) NIH_MUST( nih_strcat_sprintf(&path, NULL, "/%s", file) ); if (realpath_escapes(path, rcgpath)) { nih_error("%s: Invalid path %s", __func__, path); return -1; } // is r allowed to descend under the parent dir? if (!may_access(r.pid, r.uid, r.gid, path, O_RDONLY)) { nih_error("%s: pid %d (uid %u gid %u) may not read under %s", __func__, r.pid, r.uid, r.gid, path); return -1; } // does r have privilege over the cgroup dir? if (!may_access(r.pid, r.uid, r.gid, path, O_RDWR)) { nih_error("%s: Pid %d may not chmod %s\n", __func__, r.pid, path); return -1; } // go ahead and chmod it. if (!chmod_cgroup_path(path, mode)) { nih_error("%s: Failed to change mode on %s to %d", __func__, path, mode); return -1; } return 0; }
/** * shutdown_now: * * Send a signal to init to shut down the machine. * * This does not return. **/ static void shutdown_now (void) { nih_local char **extra_env = NULL; NihDBusError * dbus_err; if (init_halt) { char *e; e = NIH_MUST (nih_sprintf (NULL, "INIT_HALT=%s", init_halt)); extra_env = NIH_MUST (nih_str_array_new (NULL)); NIH_MUST (nih_str_array_addp (&extra_env, NULL, NULL, e)); } if (sysv_change_runlevel (runlevel, extra_env, NULL, NULL) < 0) { dbus_err = (NihDBusError *)nih_error_get (); if ((dbus_err->number != NIH_DBUS_ERROR) || strcmp (dbus_err->name, DBUS_ERROR_NO_SERVER)) { nih_fatal ("%s", dbus_err->message); nih_free (dbus_err); exit (1); } nih_free (dbus_err); /* Connection Refused means that init isn't running, this * might mean we've just upgraded to upstart and haven't * yet rebooted ... so try /dev/initctl */ sysvinit_shutdown (); } unlink (ETC_NOLOGIN); nih_main_unlink_pidfile (); exit (0); }
/* * Given a directory path, chown it to a userid. * We will chown $path and try to chown $path/tasks and $path/procs. * if @all_children is true, then chown all files under @path. (This * is for the case where the caller had the rights to mkdir the path. * In that case he gets to write to all files - the kernel will ensure * hierarhical limits) * * Return true so long as we could chown the directory itself. */ bool chown_cgroup_path(const char *path, uid_t uid, gid_t gid, bool all_children) { int len = strlen(path), ret; nih_local char *fpath = NULL; if (chown(path, uid, gid) < 0) return false; if (all_children) { struct dirent dirent, *direntp; char fpath[MAXPATHLEN]; DIR *d = opendir(path); if (len >= MAXPATHLEN) return true; strcpy(fpath, path); while (readdir_r(d, &dirent, &direntp) == 0 && direntp) { if (!strcmp(direntp->d_name, ".") || !strcmp(direntp->d_name, "..")) continue; ret = snprintf(fpath+len, MAXPATHLEN-len, "/%s", direntp->d_name); if (ret < 0 || ret >= MAXPATHLEN-len) continue; if (chown(fpath, uid, gid) < 0) nih_info("Failed to chown file %s to %d:%d", fpath, (int)uid, (int)gid); } closedir(d); } else fpath = nih_sprintf(NULL, "%s/cgroup.procs", path); if (!fpath) return true; if (chown(fpath, uid, gid) < 0) nih_info("Failed to chown procs file %s", fpath); sprintf(fpath+len, "/tasks"); if (chown(fpath, uid, gid) < 0) nih_info("Failed to chown tasks file %s", fpath); return true; }
void test_libupstart (void) { nih_local NihDBusProxy *upstart = NULL; nih_local char *version = NULL; int ret; pid_t upstart_pid; pid_t dbus_pid; char xdg_runtime_dir[PATH_MAX]; nih_local char *orig_xdg_runtime_dir = NULL; nih_local char *session_file = NULL; nih_local char *path = NULL; TEST_GROUP ("libupstart"); TEST_FEATURE ("version"); TEST_FILENAME (xdg_runtime_dir); TEST_EQ (mkdir (xdg_runtime_dir, 0755), 0); /* Take care to avoid disrupting users environment by saving and * restoring this variable (assuming the tests all pass...). */ orig_xdg_runtime_dir = getenv ("XDG_RUNTIME_DIR"); if (orig_xdg_runtime_dir) orig_xdg_runtime_dir = NIH_MUST (nih_strdup (NULL, orig_xdg_runtime_dir)); assert0 (setenv ("XDG_RUNTIME_DIR", xdg_runtime_dir, 1)); /*******************************************************************/ /* Create a private Session Init instance to connect to */ TEST_DBUS (dbus_pid); START_UPSTART (upstart_pid, TRUE); upstart = upstart_open (NULL); TEST_NE_P (upstart, NULL); /* Basic test (that does not change the state of the system * running this test) to see if we can query version of running * Upstart instance. */ ret = upstart_get_version_sync (NULL, upstart, &version); TEST_EQ (ret, 0); nih_message ("Running instance version: '%s'", version); assert0 (fnmatch ("init (upstart*)", version, 0x0)); STOP_UPSTART (upstart_pid); TEST_DBUS_END (dbus_pid); /*******************************************************************/ if (orig_xdg_runtime_dir) { /* restore */ setenv ("XDG_RUNTIME_DIR", orig_xdg_runtime_dir, 1); } else { assert0 (unsetenv ("XDG_RUNTIME_DIR")); } session_file = get_session_file (xdg_runtime_dir, upstart_pid); unlink (session_file); /* Remove the directory tree the Session Init created */ path = NIH_MUST (nih_sprintf (NULL, "%s/upstart/sessions", xdg_runtime_dir)); assert0 (rmdir (path)); path = NIH_MUST (nih_sprintf (NULL, "%s/upstart", xdg_runtime_dir)); assert0 (rmdir (path)); assert0 (rmdir (xdg_runtime_dir)); }
void test_cgroup_job_start (void) { char confdir[PATH_MAX]; char logdir[PATH_MAX]; char flagfile[PATH_MAX]; nih_local char *cmd = NULL; pid_t dbus_pid = 0; pid_t upstart_pid = 0; char **output; size_t lines; size_t len; nih_local char *logfile = NULL; nih_local char *logfile_name = NULL; nih_local char *contents = NULL; if (geteuid ()) { printf ("INFO: skipping %s tests as not running as root\n", __func__); fflush (NULL); return; } TEST_GROUP ("cgroup manager handling"); TEST_FILENAME (confdir); TEST_EQ (mkdir (confdir, 0755), 0); TEST_FILENAME (logdir); TEST_EQ (mkdir (logdir, 0755), 0); TEST_FILENAME (flagfile); /* Use the "secret" interface */ TEST_EQ (setenv ("UPSTART_CONFDIR", confdir, 1), 0); TEST_EQ (setenv ("UPSTART_LOGDIR", logdir, 1), 0); TEST_DBUS (dbus_pid); /*******************************************************************/ TEST_FEATURE ("Ensure startup job does not start until cgmanager available"); contents = nih_sprintf (NULL, "start on startup\n" "\n" "cgroup memory mem-%s\n" "\n" "exec echo hello\n", __func__); TEST_NE_P (contents, NULL); CREATE_FILE (confdir, "cgroup.conf", contents); logfile_name = NIH_MUST (nih_sprintf (NULL, "%s/%s", logdir, "cgroup.log")); start_upstart_common (&upstart_pid, FALSE, FALSE, confdir, logdir, NULL); cmd = nih_sprintf (NULL, "%s status %s 2>&1", get_initctl (), "cgroup"); TEST_NE_P (cmd, NULL); RUN_COMMAND (NULL, cmd, &output, &lines); TEST_EQ (lines, 1); /* job should *NOT* start on startup */ TEST_EQ_STR (output[0], "cgroup stop/waiting"); nih_free (output); TEST_FALSE (file_exists (logfile_name)); cmd = nih_sprintf (NULL, "%s notify-cgroup-manager-address %s 2>&1", get_initctl (), CGMANAGER_DBUS_SOCK); TEST_NE_P (cmd, NULL); RUN_COMMAND (NULL, cmd, &output, &lines); TEST_EQ (lines, 0); WAIT_FOR_FILE (logfile_name); logfile = nih_file_read (NULL, logfile_name, &len); TEST_NE_P (logfile, NULL); TEST_EQ_STR (logfile, "hello\r\n"); DELETE_FILE (confdir, "cgroup.conf"); assert0 (unlink (logfile_name)); /*******************************************************************/ TEST_FEATURE ("Ensure bogus cgroups don't crash init"); contents = nih_sprintf (NULL, "cgroup name\n" "\n" "exec echo hello\n"); TEST_NE_P (contents, NULL); CREATE_FILE (confdir, "cgroup-name.conf", contents); logfile_name = NIH_MUST (nih_sprintf (NULL, "%s/%s", logdir, "cgroup-name.log")); cmd = nih_sprintf (NULL, "%s status %s 2>&1", get_initctl (), "cgroup-name"); TEST_NE_P (cmd, NULL); RUN_COMMAND (NULL, cmd, &output, &lines); TEST_EQ (lines, 1); /* job is not running yet */ TEST_EQ_STR (output[0], "cgroup-name stop/waiting"); nih_free (output); TEST_FALSE (file_exists (logfile_name)); cmd = nih_sprintf (NULL, "%s start %s 2>&1", get_initctl (), "cgroup-name"); TEST_NE_P (cmd, NULL); RUN_COMMAND (NULL, cmd, &output, &lines); TEST_EQ (lines, 1); TEST_EQ_STR (output[0], "initctl: Job failed to start"); DELETE_FILE (confdir, "cgroup-name.conf"); /*******************************************************************/ STOP_UPSTART (upstart_pid); TEST_DBUS_END (dbus_pid); TEST_EQ (rmdir (confdir), 0); TEST_EQ (rmdir (logdir), 0); /*******************************************************************/ }
/** * session_from_dbus: * @parent: parent, * @message: D-Bus message. * * Create a new session, based on the specified D-Bus message. * * Return new Session, or NULL on error. **/ Session * session_from_dbus (const void *parent, NihDBusMessage *message) { const char *sender; DBusError dbus_error; unsigned long unix_user; unsigned long unix_process_id; char root[PATH_MAX]; Session *session; struct passwd *pwd; nih_local char *conf_path = NULL; nih_assert (message != NULL); /* Handle explicit command-line request and alternative request * method (primarily for test framework) to disable session support. */ if (disable_sessions || getenv ("UPSTART_NO_SESSIONS")) return NULL; session_init (); /* Ask D-Bus nicely for the origin uid and/or pid of the caller; * sadly we can't ask the bus daemon for the origin pid, so that * one will just have to stay user-session only. */ dbus_error_init (&dbus_error); sender = dbus_message_get_sender (message->message); if (sender) { unix_user = dbus_bus_get_unix_user (message->connection, sender, &dbus_error); if (unix_user == (unsigned long)-1) { dbus_error_free (&dbus_error); unix_user = 0; } unix_process_id = 0; } else { if (! dbus_connection_get_unix_user (message->connection, &unix_user)) unix_process_id = 0; if (! dbus_connection_get_unix_process_id (message->connection, &unix_process_id)) unix_process_id = 0; } /* If we retrieved a process id, look up the root path for it; * if it's just '/' don't worry so much about it. */ if (unix_process_id) { nih_local char *symlink = NULL; ssize_t len; symlink = NIH_MUST (nih_sprintf (NULL, "/proc/%lu/root", unix_process_id)); len = readlink (symlink, root, sizeof root); if (len < 0) return NULL; root[len] = '\0'; if (! strcmp (root, "/")) { unix_process_id = 0; if (! unix_user) return NULL; } } else if (! unix_user) { /* No process id or user id found, return the NULL session */ return NULL; } if (unix_user) { pwd = getpwuid (unix_user); if (! pwd || ! pwd->pw_dir) { nih_error ("%lu: %s: %s", unix_user, _("Unable to lookup home directory"), strerror (errno)); return NULL; } NIH_MUST (nih_strcat_sprintf (&conf_path, NULL, "%s/%s", pwd->pw_dir, USERCONFDIR)); } /* Now find in the existing Sessions list */ NIH_LIST_FOREACH_SAFE (sessions, iter) { Session *session = (Session *)iter; if (unix_process_id) { if (! session->chroot) continue; /* ignore sessions relating to other chroots */ if (strcmp (session->chroot, root)) continue; } /* ignore sessions relating to other users */ if (unix_user != session->user) continue; /* Found a user with the same uid but different * conf_dir to the existing session user. Either the * original user has been deleted and a new user created * with the same uid, or the original users home * directory has changed since they first started * running jobs. Whatever the reason, we (can only) honour * the new value. * * Since multiple users with the same uid are considered * to be "the same user", invalidate the old path, * allowing the correct new path to be set below. * * Note that there may be a possibility of trouble if * the scenario relates to a deleted user and that original * user still has jobs running. However, if that were the * case, those jobs would likely fail anyway since they * would have no working directory due to the original * users home directory being deleted/changed/made inaccessible. */ if (unix_user && conf_path && session->conf_path && strcmp (conf_path, session->conf_path)) { nih_free (session->conf_path); session->conf_path = NULL; } if (! session->conf_path) session_create_conf_source (session); return session; }
/** * wall: * @message: message to send. * * Send a message to all logged in users; based largely on the code from * bsdutils. This is done in a child process to stop anything blocking. **/ static void wall (const char *message) { struct sigaction act; struct utmpx * ent; pid_t pid; time_t now; struct tm * tm; char * user; char * tty; char hostname[MAXHOSTNAMELEN]; char * banner1; char * banner2; pid = fork (); if (pid < 0) { nih_warn (_("Unable to fork child-process to warn users: %s"), strerror (errno)); return; } else if (pid > 0) { return; } /* Break syscalls with SIGALRM */ act.sa_handler = alarm_handler; act.sa_flags = 0; sigemptyset (&act.sa_mask); sigaction (SIGALRM, &act, NULL); /* Get username for banner */ user = getlogin (); if (! user) { struct passwd *pw; pw = getpwuid (getuid ()); if (pw) user = pw->pw_name; } if (! user) { if (getuid ()) { user = NIH_MUST (nih_sprintf (NULL, "uid %d", getuid ())); } else { user = "******"; } } /* Get hostname for banner */ gethostname (hostname, sizeof (hostname)); /* Get terminal for banner */ tty = ttyname (0); if (! tty) tty = "unknown"; /* Get time */ now = time (NULL); tm = localtime (&now); /* Construct banner */ banner1 = nih_sprintf (NULL, _("Broadcast message from %s@%s"), user, hostname); banner2 = nih_sprintf (NULL, _("(%s) at %d:%02d ..."), tty, tm->tm_hour, tm->tm_min); /* Iterate entries in the utmp file */ setutxent (); while ((ent = getutxent ()) != NULL) { char dev[PATH_MAX + 1]; int fd; /* Ignore entries without a name, or not a user process */ if ((ent->ut_type != USER_PROCESS) || (! strlen (ent->ut_user))) continue; /* Construct the device path */ if (strncmp (ent->ut_line, DEV "/", 5)) { snprintf (dev, sizeof (dev), "%s/%s", DEV, ent->ut_line); } else { snprintf (dev, sizeof (dev), "%s", ent->ut_line); } alarm (2); fd = open (dev, O_WRONLY | O_NDELAY | O_NOCTTY); if ((fd >= 0) && isatty (fd)) { FILE *term; term = fdopen (fd, "w"); if (term) { fprintf (term, "\007\r\n%s\r\n\t%s\r\n\r\n", banner1, banner2); fputs (message, term); fflush (term); fclose (term); } } alarm (0); } endutxent (); nih_free (banner1); nih_free (banner2); exit (0); }
/** * warning_message: * @message: user message. * * Prefixes the message with details about how long until the shutdown * completes. * * Returns: newly allocated string. **/ static char * warning_message (const char *message) { nih_local char *banner = NULL; char * msg; nih_assert (message != NULL); if ((runlevel == '0') && init_halt && (! strcmp (init_halt, "POWEROFF"))) { if (delay) { banner = nih_sprintf ( NULL, _n("The system is going down for " "power off in %d minute!", "The system is going down for " "power off in %d minutes!", delay), delay); } else { banner = nih_strdup ( NULL, _("The system is going down for " "power off NOW!")); } } else if (runlevel == '0') { if (delay) { banner = nih_sprintf ( NULL, _n("The system is going down for " "halt in %d minute!", "The system is going down for " "halt in %d minutes!", delay), delay); } else { banner = nih_strdup ( NULL, _("The system is going down for " "halt NOW!")); } } else if (runlevel == '1') { if (delay) { banner = nih_sprintf ( NULL, _n("The system is going down for " "maintenance in %d minute!", "The system is going down for " "maintenance in %d minutes!", delay), delay); } else { banner = nih_strdup ( NULL, _("The system is going down for " "maintenance NOW!")); } } else if (runlevel == '6') { if (delay) { banner = nih_sprintf ( NULL, _n("The system is going down for " "reboot in %d minute!", "The system is going down for " "reboot in %d minutes!", delay), delay); } else { banner = nih_strdup ( NULL, _("The system is going down for " "reboot NOW!")); } } if (! banner) return NULL; msg = nih_sprintf (NULL, "\r%s\r\n%s", banner, message); return msg; }
static void udev_monitor_watcher (struct udev_monitor *udev_monitor, NihIoWatch * watch, NihIoEvents events) { struct udev_device * udev_device; nih_local char * subsystem = NULL; nih_local char * action = NULL; nih_local char * kernel = NULL; nih_local char * devpath = NULL; nih_local char * devname = NULL; nih_local char * name = NULL; nih_local char ** env = NULL; const char * value = NULL; size_t env_len = 0; DBusPendingCall * pending_call; char *(*copy_string)(const void *, const char *) = NULL; udev_device = udev_monitor_receive_device (udev_monitor); if (! udev_device) return; copy_string = no_strip_udev_data ? nih_strdup : make_safe_string; value = udev_device_get_subsystem (udev_device); subsystem = value ? copy_string (NULL, value) : NULL; value = udev_device_get_action (udev_device); action = value ? copy_string (NULL, value) : NULL; value = udev_device_get_sysname (udev_device); kernel = value ? copy_string (NULL, value) : NULL; value = udev_device_get_devpath (udev_device); devpath = value ? copy_string (NULL, value) : NULL; value = udev_device_get_devnode (udev_device); devname = value ? copy_string (NULL, value) : NULL; /* Protect against the "impossible" */ if (! action) goto out; if (! strcmp (action, "add")) { name = NIH_MUST (nih_sprintf (NULL, "%s-device-added", subsystem)); } else if (! strcmp (action, "change")) { name = NIH_MUST (nih_sprintf (NULL, "%s-device-changed", subsystem)); } else if (! strcmp (action, "remove")) { name = NIH_MUST (nih_sprintf (NULL, "%s-device-removed", subsystem)); } else { name = NIH_MUST (nih_sprintf (NULL, "%s-device-%s", subsystem, action)); } env = NIH_MUST (nih_str_array_new (NULL)); if (kernel) { nih_local char *var = NULL; var = NIH_MUST (nih_sprintf (NULL, "KERNEL=%s", kernel)); NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, var)); } if (devpath) { nih_local char *var = NULL; var = NIH_MUST (nih_sprintf (NULL, "DEVPATH=%s", devpath)); NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, var)); } if (devname) { nih_local char *var = NULL; var = NIH_MUST (nih_sprintf (NULL, "DEVNAME=%s", devname)); NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, var)); } if (subsystem) { nih_local char *var = NULL; var = NIH_MUST (nih_sprintf (NULL, "SUBSYSTEM=%s", subsystem)); NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, var)); } if (action) { nih_local char *var = NULL; var = NIH_MUST (nih_sprintf (NULL, "ACTION=%s", action)); NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, var)); } for (struct udev_list_entry *list_entry = udev_device_get_properties_list_entry (udev_device); list_entry != NULL; list_entry = udev_list_entry_get_next (list_entry)) { nih_local char *udev_name = NULL; nih_local char *udev_value = NULL; nih_local char *var = NULL; udev_name = copy_string (NULL, udev_list_entry_get_name (list_entry)); if (! strcmp (udev_name, "DEVPATH")) continue; if (! strcmp (udev_name, "DEVNAME")) continue; if (! strcmp (udev_name, "SUBSYSTEM")) continue; if (! strcmp (udev_name, "ACTION")) continue; udev_value = copy_string (NULL, udev_list_entry_get_value (list_entry)); var = NIH_MUST (nih_sprintf (NULL, "%s=%s", udev_name, udev_value)); NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, var)); } nih_debug ("%s %s", name, devname ? devname : ""); pending_call = upstart_emit_event (upstart, name, env, FALSE, NULL, emit_event_error, NULL, NIH_DBUS_TIMEOUT_NEVER); if (! pending_call) { NihError *err; int saved = errno; err = nih_error_get (); nih_warn ("%s", err->message); if (saved != ENOMEM && subsystem) nih_warn ("Likely that udev '%s' event contains binary garbage", subsystem); nih_free (err); } dbus_pending_call_unref (pending_call); out: udev_device_unref (udev_device); }