- if (reply)
- dbus_message_unref(reply);
-
- dbus_error_free(&error);
-
- return r;
-}
-
-typedef struct {
- char *name;
- char *path;
-
- char **aliases;
- char **wanted_by;
-} InstallInfo;
-
-static Hashmap *will_install = NULL, *have_installed = NULL;
-static Set *remove_symlinks_to = NULL;
-static unsigned n_symlinks = 0;
-
-static void install_info_free(InstallInfo *i) {
- assert(i);
-
- free(i->name);
- free(i->path);
- strv_free(i->aliases);
- strv_free(i->wanted_by);
- free(i);
-}
-
-static void install_info_hashmap_free(Hashmap *m) {
- InstallInfo *i;
-
- while ((i = hashmap_steal_first(m)))
- install_info_free(i);
-
- hashmap_free(m);
-}
-
-static int install_info_add(const char *name) {
- InstallInfo *i;
- int r;
-
- assert(will_install);
-
- if (!unit_name_is_valid_no_type(name, true)) {
- log_warning("Unit name %s is not a valid unit name.", name);
- return -EINVAL;
- }
-
- if (hashmap_get(have_installed, name) ||
- hashmap_get(will_install, name))
- return 0;
-
- if (!(i = new0(InstallInfo, 1))) {
- r = -ENOMEM;
- goto fail;
- }
-
- if (!(i->name = strdup(name))) {
- r = -ENOMEM;
- goto fail;
- }
-
- if ((r = hashmap_put(will_install, i->name, i)) < 0)
- goto fail;
-
- return 0;
-
-fail:
- if (i)
- install_info_free(i);
-
- return r;
-}
-
-static int config_parse_also(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- char *w;
- size_t l;
- char *state;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
-
- FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- char *n;
- int r;
-
- if (!(n = strndup(w, l)))
- return -ENOMEM;
-
- if ((r = install_info_add(n)) < 0) {
- log_warning("Cannot install unit %s: %s", n, strerror(-r));
- free(n);
- return r;
- }
-
- free(n);
- }
-
- return 0;
-}
-
-static int mark_symlink_for_removal(const char *p) {
- char *n;
- int r;
-
- assert(p);
- assert(path_is_absolute(p));
-
- if (!remove_symlinks_to)
- return 0;
-
- if (!(n = strdup(p)))
- return -ENOMEM;
-
- path_kill_slashes(n);
-
- if ((r = set_put(remove_symlinks_to, n)) < 0) {
- free(n);
- return r == -EEXIST ? 0 : r;
- }
-
- return 0;
-}
-
-static int remove_marked_symlinks_fd(int fd, const char *config_path, const char *root, bool *deleted) {
- int r = 0;
- DIR *d;
- struct dirent *de;
-
- assert(fd >= 0);
- assert(root);
- assert(deleted);
-
- if (!(d = fdopendir(fd))) {
- close_nointr_nofail(fd);
- return -errno;
- }
-
- rewinddir(d);
-
- while ((de = readdir(d))) {
- bool is_dir = false, is_link = false;
-
- if (ignore_file(de->d_name))
- continue;
-
- if (de->d_type == DT_LNK)
- is_link = true;
- else if (de->d_type == DT_DIR)
- is_dir = true;
- else if (de->d_type == DT_UNKNOWN) {
- struct stat st;
-
- if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
- log_error("Failed to stat %s/%s: %m", root, de->d_name);
-
- if (r == 0)
- r = -errno;
- continue;
- }
-
- is_link = S_ISLNK(st.st_mode);
- is_dir = S_ISDIR(st.st_mode);
- } else
- continue;
-
- if (is_dir) {
- int nfd, q;
- char *p;
-
- if ((nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW)) < 0) {
- log_error("Failed to open %s/%s: %m", root, de->d_name);
-
- if (r == 0)
- r = -errno;
- continue;
- }
-
- if (asprintf(&p, "%s/%s", root, de->d_name) < 0) {
- log_error("Failed to allocate directory string.");
- close_nointr_nofail(nfd);
- r = -ENOMEM;
- break;
- }
-
- /* This will close nfd, regardless whether it succeeds or not */
- q = remove_marked_symlinks_fd(nfd, config_path, p, deleted);
- free(p);
-
- if (r == 0)
- r = q;
-
- } else if (is_link) {
- char *p, *dest, *c;
- int q;
-
- if (asprintf(&p, "%s/%s", root, de->d_name) < 0) {
- log_error("Failed to allocate symlink string.");
- r = -ENOMEM;
- break;
- }
-
- if ((q = readlink_and_make_absolute(p, &dest)) < 0) {
- log_error("Cannot read symlink %s: %s", p, strerror(-q));
- free(p);
-
- if (r == 0)
- r = q;
- continue;
- }
-
- if ((c = canonicalize_file_name(dest))) {
- /* This might fail if the destination
- * is already removed */
-
- free(dest);
- dest = c;
- }
-
- path_kill_slashes(dest);
- if (set_get(remove_symlinks_to, dest)) {
-
- if (!arg_quiet)
- log_info("rm '%s'", p);
-
- if (unlink(p) < 0) {
- log_error("Cannot unlink symlink %s: %m", p);
-
- if (r == 0)
- r = -errno;
- } else {
- rmdir_parents(p, config_path);
- path_kill_slashes(p);
-
- if (!set_get(remove_symlinks_to, p)) {
-
- if ((r = mark_symlink_for_removal(p)) < 0) {
- if (r == 0)
- r = q;
- } else
- *deleted = true;
- }
- }
- }
-
- free(p);
- free(dest);
- }
- }
-
- closedir(d);
-
- return r;
-}
-
-static int remove_marked_symlinks(const char *config_path) {
- int fd, r = 0;
- bool deleted;
-
- assert(config_path);
-
- if (set_size(remove_symlinks_to) <= 0)
- return 0;
-
- if ((fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW)) < 0)
- return -errno;
-
- do {
- int q, cfd;
- deleted = false;
-
- if ((cfd = dup(fd)) < 0) {
- r = -errno;
- break;
- }
-
- /* This takes possession of cfd and closes it */
- if ((q = remove_marked_symlinks_fd(cfd, config_path, config_path, &deleted)) < 0) {
- if (r == 0)
- r = q;
- }
- } while (deleted);
-
- close_nointr_nofail(fd);
-
- return r;
-}
-
-static int create_symlink(const char *verb, const char *orig_old_path, const char *new_path) {
- int r;
- const char *old_path;
-
- if (arg_root)
- old_path = orig_old_path+strlen(arg_root);
- else
- old_path = orig_old_path;
-
- assert(old_path);
- assert(new_path);
- assert(verb);
-
- if (streq(verb, "enable")) {
- char *dest;
-
- mkdir_parents(new_path, 0755);
-
- if (symlink(old_path, new_path) >= 0) {
-
- if (!arg_quiet)
- log_info("ln -s '%s' '%s'", old_path, new_path);
-
- return 0;
- }
-
- if (errno != EEXIST) {
- log_error("Cannot link %s to %s: %m", old_path, new_path);
- return -errno;
- }
-
- if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
-
- if (errno == EINVAL) {
- log_error("Cannot link %s to %s, file exists already and is not a symlink.", old_path, new_path);
- return -EEXIST;
- }
-
- log_error("readlink() failed: %s", strerror(-r));
- return r;
- }
-
- if (streq(dest, old_path)) {
- free(dest);
- return 0;
- }
-
- if (!arg_force) {
- log_error("Cannot link %s to %s, symlink exists already and points to %s.", old_path, new_path, dest);
- free(dest);
- return -EEXIST;
- }
-
- free(dest);
- unlink(new_path);