+int copy_file(const char *from, const char *to) {
+ int r, fdf, fdt;
+
+ assert(from);
+ assert(to);
+
+ fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fdf < 0)
+ return -errno;
+
+ fdt = open(to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY, 0644);
+ if (fdt < 0) {
+ close_nointr_nofail(fdf);
+ return -errno;
+ }
+
+ for (;;) {
+ char buf[PIPE_BUF];
+ ssize_t n, k;
+
+ n = read(fdf, buf, sizeof(buf));
+ if (n < 0) {
+ r = -errno;
+
+ close_nointr_nofail(fdf);
+ close_nointr(fdt);
+ unlink(to);
+
+ return r;
+ }
+
+ if (n == 0)
+ break;
+
+ errno = 0;
+ k = loop_write(fdt, buf, n, false);
+ if (n != k) {
+ r = k < 0 ? k : (errno ? -errno : -EIO);
+
+ close_nointr_nofail(fdf);
+ close_nointr(fdt);
+
+ unlink(to);
+ return r;
+ }
+ }
+
+ close_nointr_nofail(fdf);
+ r = close_nointr(fdt);
+
+ if (r < 0) {
+ unlink(to);
+ return r;
+ }
+
+ return 0;
+}
+
+int symlink_or_copy(const char *from, const char *to) {
+ char *pf = NULL, *pt = NULL;
+ struct stat a, b;
+ int r;
+
+ assert(from);
+ assert(to);
+
+ if (parent_of_path(from, &pf) < 0 ||
+ parent_of_path(to, &pt) < 0) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (stat(pf, &a) < 0 ||
+ stat(pt, &b) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ if (a.st_dev != b.st_dev) {
+ free(pf);
+ free(pt);
+
+ return copy_file(from, to);
+ }
+
+ if (symlink(from, to) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ r = 0;
+
+finish:
+ free(pf);
+ free(pt);
+
+ return r;
+}
+
+int symlink_or_copy_atomic(const char *from, const char *to) {
+ char *t, *x;
+ const char *fn;
+ size_t k;
+ unsigned long long ull;
+ unsigned i;
+ int r;
+
+ assert(from);
+ assert(to);
+
+ t = new(char, strlen(to) + 1 + 16 + 1);
+ if (!t)
+ return -ENOMEM;
+
+ fn = file_name_from_path(to);
+ k = fn-to;
+ memcpy(t, to, k);
+ t[k] = '.';
+ x = stpcpy(t+k+1, fn);
+
+ ull = random_ull();
+ for (i = 0; i < 16; i++) {
+ *(x++) = hexchar(ull & 0xF);
+ ull >>= 4;
+ }
+
+ *x = 0;
+
+ r = symlink_or_copy(from, t);
+ if (r < 0) {
+ unlink(t);
+ free(t);
+ return r;
+ }
+
+ if (rename(t, to) < 0) {
+ r = -errno;
+ unlink(t);
+ free(t);
+ return r;
+ }
+
+ free(t);
+ return r;
+}
+
+int audit_session_from_pid(pid_t pid, uint32_t *id) {
+ char *p, *s;
+ uint32_t u;
+ int r;
+
+ assert(pid >= 1);
+ assert(id);
+
+ if (have_effective_cap(CAP_AUDIT_CONTROL) <= 0)
+ return -ENOENT;
+
+ if (asprintf(&p, "/proc/%lu/sessionid", (unsigned long) pid) < 0)
+ return -ENOMEM;
+
+ r = read_one_line_file(p, &s);
+ free(p);
+ if (r < 0)
+ return r;
+
+ r = safe_atou32(s, &u);
+ free(s);
+
+ if (r < 0)
+ return r;
+
+ if (u == (uint32_t) -1 || u <= 0)
+ return -ENOENT;
+
+ *id = u;
+ return 0;
+}
+
+bool display_is_local(const char *display) {
+ assert(display);
+
+ return
+ display[0] == ':' &&
+ display[1] >= '0' &&
+ display[1] <= '9';
+}
+
+int socket_from_display(const char *display, char **path) {
+ size_t k;
+ char *f, *c;
+
+ assert(display);
+ assert(path);
+
+ if (!display_is_local(display))
+ return -EINVAL;
+
+ k = strspn(display+1, "0123456789");
+
+ f = new(char, sizeof("/tmp/.X11-unix/X") + k);
+ if (!f)
+ return -ENOMEM;
+
+ c = stpcpy(f, "/tmp/.X11-unix/X");
+ memcpy(c, display+1, k);
+ c[k] = 0;
+
+ *path = f;
+
+ return 0;
+}
+
+int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home) {
+ struct passwd *p;
+ unsigned long lu;
+
+ assert(username);
+ assert(*username);
+ assert(uid);
+ assert(gid);
+ assert(home);
+
+ /* We enforce some special rules for uid=0: in order to avoid
+ * NSS lookups for root we hardcode its data. */
+
+ if (streq(*username, "root") || streq(*username, "0")) {
+ *username = "root";
+ *uid = 0;
+ *gid = 0;
+ *home = "/root";
+ return 0;
+ }
+
+ if (safe_atolu(*username, &lu) >= 0) {
+ errno = 0;
+ p = getpwuid((uid_t) lu);
+
+ /* If there are multiple users with the same id, make
+ * sure to leave $USER to the configured value instead
+ * of the first occurrence in the database. However if
+ * the uid was configured by a numeric uid, then let's
+ * pick the real username from /etc/passwd. */
+ if (p)
+ *username = p->pw_name;
+ } else {
+ errno = 0;
+ p = getpwnam(*username);
+ }
+
+ if (!p)
+ return errno != 0 ? -errno : -ESRCH;
+
+ *uid = p->pw_uid;
+ *gid = p->pw_gid;
+ *home = p->pw_dir;
+ return 0;
+}
+