chiark / gitweb /
getty: don't parse console= anymore, use /sys/class/tty/console/active instead
[elogind.git] / src / util.c
index 7f9f2b36a239be98f40ad69d0e7160b3179172d8..80b88b0e4e2fee36dc9dbf278ed919276107cc19 100644 (file)
@@ -250,7 +250,7 @@ int parse_boolean(const char *v) {
 }
 
 int parse_pid(const char *s, pid_t* ret_pid) {
-        unsigned long ul;
+        unsigned long ul = 0;
         pid_t pid;
         int r;
 
@@ -495,6 +495,9 @@ int write_one_line_file(const char *fn, const char *line) {
                 goto finish;
         }
 
+        if (!endswith(line, "\n"))
+                fputc('\n', f);
+
         r = 0;
 finish:
         fclose(f);
@@ -2614,33 +2617,6 @@ int make_null_stdio(void) {
         return make_stdio(null_fd);
 }
 
-bool is_clean_exit(int code, int status) {
-
-        if (code == CLD_EXITED)
-                return status == 0;
-
-        /* If a daemon does not implement handlers for some of the
-         * signals that's not considered an unclean shutdown */
-        if (code == CLD_KILLED)
-                return
-                        status == SIGHUP ||
-                        status == SIGINT ||
-                        status == SIGTERM ||
-                        status == SIGPIPE;
-
-        return false;
-}
-
-bool is_clean_exit_lsb(int code, int status) {
-
-        if (is_clean_exit(code, status))
-                return true;
-
-        return
-                code == CLD_EXITED &&
-                (status == EXIT_NOTINSTALLED || status == EXIT_NOTCONFIGURED);
-}
-
 bool is_device_path(const char *path) {
 
         /* Returns true on paths that refer to a device, either in
@@ -2979,80 +2955,132 @@ void status_printf(const char *format, ...) {
 }
 
 void status_welcome(void) {
+        char *pretty_name = NULL, *ansi_color = NULL;
+        const char *const_pretty = NULL, *const_color = NULL;
+        int r;
 
-#if defined(TARGET_FEDORA)
-        char *r;
+        if ((r = parse_env_file("/etc/os-release", NEWLINE,
+                                "PRETTY_NAME", &pretty_name,
+                                "ANSI_COLOR", &ansi_color,
+                                NULL)) < 0) {
 
-        if (read_one_line_file("/etc/system-release", &r) < 0)
-                return;
+                if (r != -ENOENT)
+                        log_warning("Failed to read /etc/os-release: %s", strerror(-r));
+        }
+
+#if defined(TARGET_FEDORA)
+        if (!pretty_name) {
+                if ((r = read_one_line_file("/etc/system-release", &pretty_name)) < 0) {
 
-        truncate_nl(r);
+                        if (r != -ENOENT)
+                                log_warning("Failed to read /etc/system-release: %s", strerror(-r));
+                } else
+                        truncate_nl(pretty_name);
+        }
 
-        /* This tries to mimic the color magic the old Red Hat sysinit
-         * script did. */
+        if (!ansi_color && pretty_name) {
 
-        if (startswith(r, "Red Hat"))
-                status_printf("Welcome to \x1B[0;31m%s\x1B[0m!\n", r); /* Red for RHEL */
-        else if (startswith(r, "Fedora"))
-                status_printf("Welcome to \x1B[0;34m%s\x1B[0m!\n", r); /* Blue for Fedora */
-        else
-                status_printf("Welcome to \x1B[1m%s\x1B[0m!\n", r); /* Highlight for everything else */
+                /* This tries to mimic the color magic the old Red Hat sysinit
+                 * script did. */
 
-        free(r);
+                if (startswith(pretty_name, "Red Hat"))
+                        const_color = "0;31"; /* Red for RHEL */
+                else if (startswith(pretty_name, "Fedora"))
+                        const_color = "0;34"; /* Blue for Fedora */
+        }
 
 #elif defined(TARGET_SUSE)
-        char *r;
 
-        if (read_one_line_file("/etc/SuSE-release", &r) < 0)
-                return;
+        if (!pretty_name) {
+                if ((r = read_one_line_file("/etc/SuSE-release", &pretty_name)) < 0) {
 
-        truncate_nl(r);
+                        if (r != -ENOENT)
+                                log_warning("Failed to read /etc/SuSE-release: %s", strerror(-r));
+                } else
+                        truncate_nl(pretty_name);
+        }
 
-        status_printf("Welcome to \x1B[0;32m%s\x1B[0m!\n", r); /* Green for SUSE */
-        free(r);
+        if (!ansi_color)
+                const_color = "0;32"; /* Green for openSUSE */
 
 #elif defined(TARGET_GENTOO)
-        char *r;
 
-        if (read_one_line_file("/etc/gentoo-release", &r) < 0)
-                return;
+        if (!pretty_name) {
+                if ((r = read_one_line_file("/etc/gentoo-release", &pretty_name)) < 0) {
 
-        truncate_nl(r);
+                        if (r != -ENOENT)
+                                log_warning("Failed to read /etc/gentoo-release: %s", strerror(-r));
+                } else
+                        truncate_nl(pretty_name);
+        }
 
-        status_printf("Welcome to \x1B[1;34m%s\x1B[0m!\n", r); /* Light Blue for Gentoo */
+        if (!ansi_color)
+                const_color = "1;34"; /* Light Blue for Gentoo */
+
+#elif defined(TARGET_ALTLINUX)
+
+        if (!pretty_name) {
+                if ((r = read_one_line_file("/etc/altlinux-release", &pretty_name)) < 0) {
+
+                        if (r != -ENOENT)
+                                log_warning("Failed to read /etc/altlinux-release: %s", strerror(-r));
+                } else
+                        truncate_nl(pretty_name);
+        }
+
+        if (!ansi_color)
+                const_color = "0;36"; /* Cyan for ALTLinux */
 
-        free(r);
 
 #elif defined(TARGET_DEBIAN)
-        char *r;
 
-        if (read_one_line_file("/etc/debian_version", &r) < 0)
-                return;
+        if (!pretty_name) {
+                char *version;
 
-        truncate_nl(r);
+                if ((r = read_one_line_file("/etc/debian_version", &version)) < 0) {
 
-        status_printf("Welcome to Debian \x1B[1;31m%s\x1B[0m!\n", r); /* Light Red for Debian */
+                        if (r != -ENOENT)
+                                log_warning("Failed to read /etc/debian_version: %s", strerror(-r));
+                } else {
+                        truncate_nl(version);
+                        pretty_name = strappend("Debian ", version);
+                        free(version);
+
+                        if (!pretty_name)
+                                log_warning("Failed to allocate Debian version string.");
+                }
+        }
+
+        if (!ansi_color)
+                const_color = "1;31"; /* Light Red for Debian */
 
-        free(r);
 #elif defined(TARGET_UBUNTU)
-        char *desc = NULL;
-        char *codename = NULL;
 
-        if (parse_env_file("/etc/lsb-release", NEWLINE,
-                "DISTRIB_DESCRIPTION", &desc,
-                "DISTRIB_CODENAME", &codename, NULL) < 0)
-                return;
-        if (desc && codename)
-                /* Light Red for Ubuntu */
-                status_printf("Welcome to \x1B[1;31m%s\x1B[0m (%s)\n",
-                        desc, codename);
-        free(desc);
-        free(codename);
-#elif defined(TARGET_ARCH)
-        status_printf("Welcome to \x1B[1;36mArch Linux\x1B[0m!\n"); /* Cyan for Arch */
-#else
-#warning "You probably should add a welcome text logic here."
+        if ((r = parse_env_file("/etc/lsb-release", NEWLINE,
+                                "DISTRIB_DESCRIPTION", &pretty_name,
+                                NULL)) < 0) {
+
+                if (r != -ENOENT)
+                        log_warning("Failed to read /etc/lsb-release: %s", strerror(-r));
+        }
+
+        if (!ansi_color)
+                const_color = "0;33"; /* Orange/Brown for Ubuntu */
+
 #endif
+
+        if (!pretty_name && !const_pretty)
+                const_pretty = "Linux";
+
+        if (!ansi_color && !const_color)
+                const_color = "1";
+
+        status_printf("Welcome to \x1B[%sm%s\x1B[0m!\n",
+                      const_color ? const_color : ansi_color,
+                      const_pretty ? const_pretty : pretty_name);
+
+        free(ansi_color);
+        free(pretty_name);
 }
 
 char *replace_env(const char *format, char **env) {
@@ -3289,6 +3317,44 @@ char *unquote(const char *s, const char* quotes) {
         return strdup(s);
 }
 
+char *normalize_env_assignment(const char *s) {
+        char *name, *value, *p, *r;
+
+        p = strchr(s, '=');
+
+        if (!p) {
+                if (!(r = strdup(s)))
+                        return NULL;
+
+                return strstrip(r);
+        }
+
+        if (!(name = strndup(s, p - s)))
+                return NULL;
+
+        if (!(p = strdup(p+1))) {
+                free(name);
+                return NULL;
+        }
+
+        value = unquote(strstrip(p), QUOTES);
+        free(p);
+
+        if (!value) {
+                free(p);
+                free(name);
+                return NULL;
+        }
+
+        if (asprintf(&r, "%s=%s", name, value) < 0)
+                r = NULL;
+
+        free(value);
+        free(name);
+
+        return r;
+}
+
 int wait_for_terminate(pid_t pid, siginfo_t *status) {
         assert(pid >= 1);
         assert(status);
@@ -3342,6 +3408,8 @@ int wait_for_terminate_and_warn(const char *name, pid_t pid) {
 }
 
 void freeze(void) {
+        sync();
+
         for (;;)
                 pause();
 }
@@ -3358,8 +3426,19 @@ bool null_or_empty(struct stat *st) {
         return false;
 }
 
-DIR *xopendirat(int fd, const char *name) {
-        return fdopendir(openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
+DIR *xopendirat(int fd, const char *name, int flags) {
+        int nfd;
+        DIR *d;
+
+        if ((nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags)) < 0)
+                return NULL;
+
+        if (!(d = fdopendir(nfd))) {
+                close_nointr_nofail(nfd);
+                return NULL;
+        }
+
+        return d;
 }
 
 int signal_from_string_try_harder(const char *s) {
@@ -3440,7 +3519,7 @@ char *fstab_node_to_udev_node(const char *p) {
                 if (!t)
                         return NULL;
 
-                r = asprintf(&dn, "/dev/disk/by-uuid/%s", ascii_strlower(t));
+                r = asprintf(&dn, "/dev/disk/by-uuid/%s", t);
                 free(t);
 
                 if (r < 0)
@@ -3470,6 +3549,121 @@ void filter_environ(const char *prefix) {
         environ[j] = NULL;
 }
 
+bool tty_is_vc(const char *tty) {
+        assert(tty);
+
+        if (startswith(tty, "/dev/"))
+                tty += 5;
+
+        return startswith(tty, "tty") &&
+                tty[3] >= '0' && tty[3] <= '9';
+}
+
+const char *default_term_for_tty(const char *tty) {
+        char *active = NULL;
+        const char *term;
+
+        assert(tty);
+
+        if (startswith(tty, "/dev/"))
+                tty += 5;
+
+        /* Resolve where /dev/console is pointing when determining
+         * TERM */
+        if (streq(tty, "console"))
+                if (read_one_line_file("/sys/class/tty/console/active", &active) >= 0) {
+                        truncate_nl(active);
+
+                        /* If multiple log outputs are configured the
+                         * last one is what /dev/console points to */
+                        if ((tty = strrchr(active, ' ')))
+                                tty++;
+                        else
+                                tty = active;
+                }
+
+        term = tty_is_vc(tty) ? "TERM=linux" : "TERM=vt100";
+        free(active);
+
+        return term;
+}
+
+bool running_in_vm(void) {
+
+#if defined(__i386__) || defined(__x86_64__)
+
+        /* Both CPUID and DMI are x86 specific interfaces... */
+
+        const char *const dmi_vendors[] = {
+                "/sys/class/dmi/id/sys_vendor",
+                "/sys/class/dmi/id/board_vendor",
+                "/sys/class/dmi/id/bios_vendor"
+        };
+
+        uint32_t eax = 0x40000000;
+        union {
+                uint32_t sig32[3];
+                char text[13];
+        } sig;
+
+        unsigned i;
+
+        for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
+                char *s;
+                bool b;
+
+                if (read_one_line_file(dmi_vendors[i], &s) < 0)
+                        continue;
+
+                b = startswith(s, "QEMU") ||
+                        /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
+                        startswith(s, "VMware") ||
+                        startswith(s, "VMW") ||
+                        startswith(s, "Microsoft Corporation") ||
+                        startswith(s, "innotek GmbH") ||
+                        startswith(s, "Xen");
+
+                free(s);
+
+                if (b)
+                        return true;
+        }
+
+        /* http://lwn.net/Articles/301888/ */
+        zero(sig);
+
+
+#if defined (__i386__)
+#define REG_a "eax"
+#define REG_b "ebx"
+#elif defined (__amd64__)
+#define REG_a "rax"
+#define REG_b "rbx"
+#endif
+
+        __asm__ __volatile__ (
+                /* ebx/rbx is being used for PIC! */
+                "  push %%"REG_b"         \n\t"
+                "  cpuid                  \n\t"
+                "  mov %%ebx, %1          \n\t"
+                "  pop %%"REG_b"          \n\t"
+
+                : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
+                : "0" (eax)
+        );
+
+        if (streq(sig.text, "XenVMMXenVMM") ||
+            streq(sig.text, "KVMKVMKVM") ||
+            /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
+            streq(sig.text, "VMwareVMware") ||
+            /* http://msdn.microsoft.com/en-us/library/bb969719.aspx */
+            streq(sig.text, "Microsoft Hv"))
+                return true;
+#endif
+
+        return false;
+}
+
 static const char *const ioprio_class_table[] = {
         [IOPRIO_CLASS_NONE] = "none",
         [IOPRIO_CLASS_RT] = "realtime",