move more common files to shared/ and add them to shared.la

This commit is contained in:
Kay Sievers
2012-04-12 02:39:37 +02:00
parent 5e6f883dfc
commit f33d3ec1d7
33 changed files with 62 additions and 99 deletions
+261
View File
@@ -0,0 +1,261 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include "util.h"
#include "macro.h"
#include "cgroup-util.h"
#include "cgroup-show.h"
static int compare(const void *a, const void *b) {
const pid_t *p = a, *q = b;
if (*p < *q)
return -1;
if (*p > *q)
return 1;
return 0;
}
static unsigned ilog10(unsigned long ul) {
int n = 0;
while (ul > 0) {
n++;
ul /= 10;
}
return n;
}
static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigned n_columns, bool more, bool kernel_threads) {
char *fn;
FILE *f;
size_t n = 0, n_allocated = 0;
pid_t *pids = NULL;
char *p;
pid_t pid, biggest = 0;
int r;
if (n_columns <= 0)
n_columns = columns();
if (!prefix)
prefix = "";
if ((r = cg_fix_path(path, &p)) < 0)
return r;
r = asprintf(&fn, "%s/cgroup.procs", p);
free(p);
if (r < 0)
return -ENOMEM;
f = fopen(fn, "re");
free(fn);
if (!f)
return -errno;
while ((r = cg_read_pid(f, &pid)) > 0) {
if (!kernel_threads && is_kernel_thread(pid) > 0)
continue;
if (n >= n_allocated) {
pid_t *npids;
n_allocated = MAX(16U, n*2U);
if (!(npids = realloc(pids, sizeof(pid_t) * n_allocated))) {
r = -ENOMEM;
goto finish;
}
pids = npids;
}
assert(n < n_allocated);
pids[n++] = pid;
if (pid > biggest)
biggest = pid;
}
if (r < 0)
goto finish;
if (n > 0) {
unsigned i, m;
/* Filter duplicates */
m = 0;
for (i = 0; i < n; i++) {
unsigned j;
for (j = i+1; j < n; j++)
if (pids[i] == pids[j])
break;
if (j >= n)
pids[m++] = pids[i];
}
n = m;
/* And sort */
qsort(pids, n, sizeof(pid_t), compare);
if (n_columns > 8)
n_columns -= 8;
else
n_columns = 20;
for (i = 0; i < n; i++) {
char *t = NULL;
get_process_cmdline(pids[i], n_columns, true, &t);
printf("%s%s %*lu %s\n",
prefix,
(more || i < n-1) ? "\342\224\234" : "\342\224\224",
(int) ilog10(biggest),
(unsigned long) pids[i],
strna(t));
free(t);
}
}
r = 0;
finish:
free(pids);
if (f)
fclose(f);
return r;
}
int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns, bool kernel_threads) {
DIR *d;
char *last = NULL;
char *p1 = NULL, *p2 = NULL, *fn = NULL, *gn = NULL;
bool shown_pids = false;
int r;
if (n_columns <= 0)
n_columns = columns();
if (!prefix)
prefix = "";
if ((r = cg_fix_path(path, &fn)) < 0)
return r;
if (!(d = opendir(fn))) {
free(fn);
return -errno;
}
while ((r = cg_read_subgroup(d, &gn)) > 0) {
if (!shown_pids) {
show_cgroup_one_by_path(path, prefix, n_columns, true, kernel_threads);
shown_pids = true;
}
if (last) {
printf("%s\342\224\234 %s\n", prefix, file_name_from_path(last));
if (!p1)
if (!(p1 = strappend(prefix, "\342\224\202 "))) {
r = -ENOMEM;
goto finish;
}
show_cgroup_by_path(last, p1, n_columns-2, kernel_threads);
free(last);
last = NULL;
}
r = asprintf(&last, "%s/%s", fn, gn);
free(gn);
if (r < 0) {
r = -ENOMEM;
goto finish;
}
}
if (r < 0)
goto finish;
if (!shown_pids)
show_cgroup_one_by_path(path, prefix, n_columns, !!last, kernel_threads);
if (last) {
printf("%s\342\224\224 %s\n", prefix, file_name_from_path(last));
if (!p2)
if (!(p2 = strappend(prefix, " "))) {
r = -ENOMEM;
goto finish;
}
show_cgroup_by_path(last, p2, n_columns-2, kernel_threads);
}
r = 0;
finish:
free(p1);
free(p2);
free(last);
free(fn);
closedir(d);
return r;
}
int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads) {
char *p;
int r;
assert(controller);
assert(path);
r = cg_get_path(controller, path, NULL, &p);
if (r < 0)
return r;
r = show_cgroup_by_path(p, prefix, n_columns, kernel_threads);
free(p);
return r;
}
+30
View File
@@ -0,0 +1,30 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#ifndef foocgroupshowhfoo
#define foocgroupshowhfoo
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdbool.h>
int show_cgroup_by_path(const char *path, const char *prefix, unsigned columns, bool kernel_threads);
int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned columns, bool kernel_threads);
#endif
File diff suppressed because it is too large Load Diff
+198
View File
@@ -0,0 +1,198 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#ifndef foodbuscommonhfoo
#define foodbuscommonhfoo
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <dbus/dbus.h>
#ifndef DBUS_ERROR_UNKNOWN_OBJECT
#define DBUS_ERROR_UNKNOWN_OBJECT "org.freedesktop.DBus.Error.UnknownObject"
#endif
#ifndef DBUS_ERROR_UNKNOWN_INTERFACE
#define DBUS_ERROR_UNKNOWN_INTERFACE "org.freedesktop.DBus.Error.UnknownInterface"
#endif
#ifndef DBUS_ERROR_UNKNOWN_PROPERTY
#define DBUS_ERROR_UNKNOWN_PROPERTY "org.freedesktop.DBus.Error.UnknownProperty"
#endif
#ifndef DBUS_ERROR_PROPERTY_READ_ONLY
#define DBUS_ERROR_PROPERTY_READ_ONLY "org.freedesktop.DBus.Error.PropertyReadOnly"
#endif
#define BUS_PROPERTIES_INTERFACE \
" <interface name=\"org.freedesktop.DBus.Properties\">\n" \
" <method name=\"Get\">\n" \
" <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n" \
" <arg name=\"property\" direction=\"in\" type=\"s\"/>\n" \
" <arg name=\"value\" direction=\"out\" type=\"v\"/>\n" \
" </method>\n" \
" <method name=\"GetAll\">\n" \
" <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n" \
" <arg name=\"properties\" direction=\"out\" type=\"a{sv}\"/>\n" \
" </method>\n" \
" <method name=\"Set\">\n" \
" <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n" \
" <arg name=\"property\" direction=\"in\" type=\"s\"/>\n" \
" <arg name=\"value\" direction=\"in\" type=\"v\"/>\n" \
" </method>\n" \
" <signal name=\"PropertiesChanged\">\n" \
" <arg type=\"s\" name=\"interface\"/>\n" \
" <arg type=\"a{sv}\" name=\"changed_properties\"/>\n" \
" <arg type=\"as\" name=\"invalidated_properties\"/>\n" \
" </signal>\n" \
" </interface>\n"
#define BUS_INTROSPECTABLE_INTERFACE \
" <interface name=\"org.freedesktop.DBus.Introspectable\">\n" \
" <method name=\"Introspect\">\n" \
" <arg name=\"data\" type=\"s\" direction=\"out\"/>\n" \
" </method>\n" \
" </interface>\n"
#define BUS_PEER_INTERFACE \
"<interface name=\"org.freedesktop.DBus.Peer\">\n" \
" <method name=\"Ping\"/>\n" \
" <method name=\"GetMachineId\">\n" \
" <arg type=\"s\" name=\"machine_uuid\" direction=\"out\"/>\n" \
" </method>\n" \
"</interface>\n"
#define BUS_GENERIC_INTERFACES_LIST \
"org.freedesktop.DBus.Properties\0" \
"org.freedesktop.DBus.Introspectable\0" \
"org.freedesktop.DBus.Peer\0"
int bus_check_peercred(DBusConnection *c);
int bus_connect(DBusBusType t, DBusConnection **_bus, bool *private_bus, DBusError *error);
int bus_connect_system_ssh(const char *user, const char *host, DBusConnection **_bus, DBusError *error);
int bus_connect_system_polkit(DBusConnection **_bus, DBusError *error);
const char *bus_error_message(const DBusError *error);
typedef int (*BusPropertyCallback)(DBusMessageIter *iter, const char *property, void *data);
typedef int (*BusPropertySetCallback)(DBusMessageIter *iter, const char *property, void *data);
typedef struct BusProperty {
const char *property; /* name of the property */
BusPropertyCallback append; /* Function that is called to serialize this property */
const char *signature;
const uint16_t offset; /* Offset from BusBoundProperties::base address to the property data.
* uint16_t is sufficient, because we have no structs too big.
* -Werror=overflow will catch it if this does not hold. */
bool indirect; /* data is indirect, ie. not base+offset, but *(base+offset) */
BusPropertySetCallback set; /* Optional: Function that is called to set this property */
} BusProperty;
typedef struct BusBoundProperties {
const char *interface; /* interface of the properties */
const BusProperty *properties; /* array of properties, ended by a NULL-filled element */
const void *const base; /* base pointer to which the offset must be added to reach data */
} BusBoundProperties;
DBusHandlerResult bus_send_error_reply(
DBusConnection *c,
DBusMessage *message,
DBusError *bus_error,
int error);
DBusHandlerResult bus_default_message_handler(
DBusConnection *c,
DBusMessage *message,
const char *introspection,
const char *interfaces,
const BusBoundProperties *bound_properties);
int bus_property_append_string(DBusMessageIter *i, const char *property, void *data);
int bus_property_append_strv(DBusMessageIter *i, const char *property, void *data);
int bus_property_append_bool(DBusMessageIter *i, const char *property, void *data);
int bus_property_append_tristate_false(DBusMessageIter *i, const char *property, void *data);
int bus_property_append_int32(DBusMessageIter *i, const char *property, void *data);
int bus_property_append_uint32(DBusMessageIter *i, const char *property, void *data);
int bus_property_append_uint64(DBusMessageIter *i, const char *property, void *data);
int bus_property_append_size(DBusMessageIter *i, const char *property, void *data);
int bus_property_append_ul(DBusMessageIter *i, const char *property, void *data);
int bus_property_append_long(DBusMessageIter *i, const char *property, void *data);
#define bus_property_append_int bus_property_append_int32
#define bus_property_append_pid bus_property_append_uint32
#define bus_property_append_uid bus_property_append_uint32
#define bus_property_append_gid bus_property_append_uint32
#define bus_property_append_mode bus_property_append_uint32
#define bus_property_append_unsigned bus_property_append_uint32
#define bus_property_append_usec bus_property_append_uint64
#define DEFINE_BUS_PROPERTY_APPEND_ENUM(function,name,type) \
int function(DBusMessageIter *i, const char *property, void *data) { \
const char *value; \
type *field = data; \
\
assert(i); \
assert(property); \
\
value = name##_to_string(*field); \
\
if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &value)) \
return -ENOMEM; \
\
return 0; \
}
#define DEFINE_BUS_PROPERTY_SET_ENUM(function,name,type) \
int function(DBusMessageIter *i, const char *property, void *data) { \
const char *value; \
type *field = data; \
\
assert(i); \
assert(property); \
\
dbus_message_iter_get_basic(i, &value); \
\
*field = name##_from_string(value); \
\
return 0; \
}
const char *bus_errno_to_dbus(int error);
DBusMessage* bus_properties_changed_new(const char *path, const char *interface, const char *properties);
uint32_t bus_flags_to_events(DBusWatch *bus_watch);
unsigned bus_events_to_flags(uint32_t events);
int bus_parse_strv(DBusMessage *m, char ***_l);
int bus_parse_strv_iter(DBusMessageIter *iter, char ***_l);
int bus_append_strv_iter(DBusMessageIter *iter, char **l);
int bus_iter_get_basic_and_next(DBusMessageIter *iter, int type, void *data, bool next);
int generic_print_property(const char *name, DBusMessageIter *iter, bool all);
void bus_async_unregister_and_exit(DBusConnection *bus, const char *name);
DBusHandlerResult bus_exit_idle_filter(DBusConnection *bus, DBusMessage *m, void *userdata);
#endif
+187
View File
@@ -0,0 +1,187 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include "hostname-setup.h"
#include "macro.h"
#include "util.h"
#include "log.h"
#if defined(TARGET_FEDORA) || defined(TARGET_ALTLINUX) || defined(TARGET_MANDRIVA) || defined(TARGET_MEEGO) || defined(TARGET_MAGEIA)
#define FILENAME "/etc/sysconfig/network"
#elif defined(TARGET_SUSE) || defined(TARGET_SLACKWARE)
#define FILENAME "/etc/HOSTNAME"
#elif defined(TARGET_ARCH)
#define FILENAME "/etc/rc.conf"
#elif defined(TARGET_GENTOO)
#define FILENAME "/etc/conf.d/hostname"
#endif
static int read_and_strip_hostname(const char *path, char **hn) {
char *s;
int r;
assert(path);
assert(hn);
if ((r = read_one_line_file(path, &s)) < 0)
return r;
hostname_cleanup(s);
if (isempty(s)) {
free(s);
return -ENOENT;
}
*hn = s;
return 0;
}
static int read_distro_hostname(char **hn) {
#if defined(TARGET_FEDORA) || defined(TARGET_ARCH) || defined(TARGET_GENTOO) || defined(TARGET_ALTLINUX) || defined(TARGET_MANDRIVA) || defined(TARGET_MEEGO) || defined(TARGET_MAGEIA)
int r;
FILE *f;
assert(hn);
if (!(f = fopen(FILENAME, "re")))
return -errno;
for (;;) {
char line[LINE_MAX];
char *s, *k;
if (!fgets(line, sizeof(line), f)) {
if (feof(f))
break;
r = -errno;
goto finish;
}
s = strstrip(line);
if (!startswith_no_case(s, "HOSTNAME="))
continue;
if (!(k = strdup(s+9))) {
r = -ENOMEM;
goto finish;
}
hostname_cleanup(k);
if (isempty(k)) {
free(k);
r = -ENOENT;
goto finish;
}
*hn = k;
r = 0;
goto finish;
}
r = -ENOENT;
finish:
fclose(f);
return r;
#elif defined(TARGET_SUSE) || defined(TARGET_SLACKWARE)
return read_and_strip_hostname(FILENAME, hn);
#else
return -ENOENT;
#endif
}
static int read_hostname(char **hn) {
int r;
assert(hn);
/* First, try to load the generic hostname configuration file,
* that we support on all distributions */
if ((r = read_and_strip_hostname("/etc/hostname", hn)) < 0) {
if (r == -ENOENT)
return read_distro_hostname(hn);
return r;
}
return 0;
}
int hostname_setup(void) {
int r;
char *b = NULL;
const char *hn = NULL;
if ((r = read_hostname(&b)) < 0) {
if (r == -ENOENT)
log_info("No hostname configured.");
else
log_warning("Failed to read configured hostname: %s", strerror(-r));
hn = NULL;
} else
hn = b;
if (!hn) {
/* Don't override the hostname if it is unset and not
* explicitly configured */
char *old_hostname = NULL;
if ((old_hostname = gethostname_malloc())) {
bool already_set;
already_set = old_hostname[0] != 0;
free(old_hostname);
if (already_set)
goto finish;
}
hn = "localhost";
}
if (sethostname(hn, strlen(hn)) < 0) {
log_warning("Failed to set hostname to <%s>: %m", hn);
r = -errno;
} else
log_info("Set hostname to <%s>.", hn);
finish:
free(b);
return r;
}
+27
View File
@@ -0,0 +1,27 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#ifndef foohostnamesetuphfoo
#define foohostnamesetuphfoo
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
int hostname_setup(void);
#endif
+1954
View File
File diff suppressed because it is too large Load Diff
+89
View File
@@ -0,0 +1,89 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#ifndef fooinstallhfoo
#define fooinstallhfoo
/***
This file is part of systemd.
Copyright 2011 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "hashmap.h"
typedef enum UnitFileScope {
UNIT_FILE_SYSTEM,
UNIT_FILE_GLOBAL,
UNIT_FILE_USER,
_UNIT_FILE_SCOPE_MAX,
_UNIT_FILE_SCOPE_INVALID = -1
} UnitFileScope;
typedef enum UnitFileState {
UNIT_FILE_ENABLED,
UNIT_FILE_ENABLED_RUNTIME,
UNIT_FILE_LINKED,
UNIT_FILE_LINKED_RUNTIME,
UNIT_FILE_MASKED,
UNIT_FILE_MASKED_RUNTIME,
UNIT_FILE_STATIC,
UNIT_FILE_DISABLED,
_UNIT_FILE_STATE_MAX,
_UNIT_FILE_STATE_INVALID = -1
} UnitFileState;
typedef enum UnitFileChangeType {
UNIT_FILE_SYMLINK,
UNIT_FILE_UNLINK,
_UNIT_FILE_CHANGE_TYPE_MAX,
_UNIT_FILE_CHANGE_TYPE_INVALID = -1
} UnitFileChangeType;
typedef struct UnitFileChange {
UnitFileChangeType type;
char *path;
char *source;
} UnitFileChange;
typedef struct UnitFileList {
char *path;
UnitFileState state;
} UnitFileList;
int unit_file_enable(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes);
int unit_file_disable(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], UnitFileChange **changes, unsigned *n_changes);
int unit_file_reenable(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes);
int unit_file_link(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes);
int unit_file_preset(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes);
int unit_file_mask(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes);
int unit_file_unmask(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], UnitFileChange **changes, unsigned *n_changes);
UnitFileState unit_file_get_state(UnitFileScope scope, const char *root_dir, const char *filename);
int unit_file_get_list(UnitFileScope scope, const char *root_dir, Hashmap *h);
void unit_file_list_free(Hashmap *h);
void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes);
int unit_file_query_preset(UnitFileScope scope, const char *name);
const char *unit_file_state_to_string(UnitFileState s);
UnitFileState unit_file_state_from_string(const char *s);
const char *unit_file_change_type_to_string(UnitFileChangeType s);
UnitFileChangeType unit_file_change_type_from_string(const char *s);
#endif
+677
View File
@@ -0,0 +1,677 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2012 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <time.h>
#include <assert.h>
#include <errno.h>
#include <sys/poll.h>
#include <string.h>
#include "logs-show.h"
#include "log.h"
#include "util.h"
#define PRINT_THRESHOLD 128
static bool contains_unprintable(const void *p, size_t l) {
const char *j;
for (j = p; j < (const char *) p + l; j++)
if (*j < ' ' || *j >= 127)
return true;
return false;
}
static int parse_field(const void *data, size_t length, const char *field, char **target, size_t *target_size) {
size_t fl, nl;
void *buf;
assert(data);
assert(field);
assert(target);
assert(target_size);
fl = strlen(field);
if (length < fl)
return 0;
if (memcmp(data, field, fl))
return 0;
nl = length - fl;
buf = malloc(nl+1);
memcpy(buf, (const char*) data + fl, nl);
((char*)buf)[nl] = 0;
if (!buf) {
log_error("Out of memory");
return -ENOMEM;
}
free(*target);
*target = buf;
*target_size = nl;
return 1;
}
static bool shall_print(bool show_all, char *p, size_t l) {
if (show_all)
return true;
if (l > PRINT_THRESHOLD)
return false;
if (contains_unprintable(p, l))
return false;
return true;
}
static int output_short(sd_journal *j, unsigned line, unsigned n_columns, bool show_all, bool monotonic_mode) {
int r;
const void *data;
size_t length;
size_t n = 0;
char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL;
size_t hostname_len = 0, identifier_len = 0, comm_len = 0, pid_len = 0, fake_pid_len = 0, message_len = 0, realtime_len = 0, monotonic_len = 0;
assert(j);
SD_JOURNAL_FOREACH_DATA(j, data, length) {
r = parse_field(data, length, "_HOSTNAME=", &hostname, &hostname_len);
if (r < 0)
goto finish;
else if (r > 0)
continue;
r = parse_field(data, length, "SYSLOG_IDENTIFIER=", &identifier, &identifier_len);
if (r < 0)
goto finish;
else if (r > 0)
continue;
r = parse_field(data, length, "_COMM=", &comm, &comm_len);
if (r < 0)
goto finish;
else if (r > 0)
continue;
r = parse_field(data, length, "_PID=", &pid, &pid_len);
if (r < 0)
goto finish;
else if (r > 0)
continue;
r = parse_field(data, length, "SYSLOG_PID=", &fake_pid, &fake_pid_len);
if (r < 0)
goto finish;
else if (r > 0)
continue;
r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len);
if (r < 0)
goto finish;
else if (r > 0)
continue;
r = parse_field(data, length, "_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len);
if (r < 0)
goto finish;
else if (r > 0)
continue;
r = parse_field(data, length, "MESSAGE=", &message, &message_len);
if (r < 0)
goto finish;
}
if (!message) {
r = 0;
goto finish;
}
if (monotonic_mode) {
uint64_t t;
sd_id128_t boot_id;
r = -ENOENT;
if (monotonic)
r = safe_atou64(monotonic, &t);
if (r < 0)
r = sd_journal_get_monotonic_usec(j, &t, &boot_id);
if (r < 0) {
log_error("Failed to get monotonic: %s", strerror(-r));
goto finish;
}
printf("[%5llu.%06llu]",
(unsigned long long) (t / USEC_PER_SEC),
(unsigned long long) (t % USEC_PER_SEC));
n += 1 + 5 + 1 + 6 + 1;
} else {
char buf[64];
uint64_t x;
time_t t;
struct tm tm;
r = -ENOENT;
if (realtime)
r = safe_atou64(realtime, &x);
if (r < 0)
r = sd_journal_get_realtime_usec(j, &x);
if (r < 0) {
log_error("Failed to get realtime: %s", strerror(-r));
goto finish;
}
t = (time_t) (x / USEC_PER_SEC);
if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm)) <= 0) {
log_error("Failed to format time.");
goto finish;
}
fputs(buf, stdout);
n += strlen(buf);
}
if (hostname && shall_print(show_all, hostname, hostname_len)) {
printf(" %.*s", (int) hostname_len, hostname);
n += hostname_len + 1;
}
if (identifier && shall_print(show_all, identifier, identifier_len)) {
printf(" %.*s", (int) identifier_len, identifier);
n += identifier_len + 1;
} else if (comm && shall_print(show_all, comm, comm_len)) {
printf(" %.*s", (int) comm_len, comm);
n += comm_len + 1;
} else
putchar(' ');
if (pid && shall_print(show_all, pid, pid_len)) {
printf("[%.*s]", (int) pid_len, pid);
n += pid_len + 2;
} else if (fake_pid && shall_print(show_all, fake_pid, fake_pid_len)) {
printf("[%.*s]", (int) fake_pid_len, fake_pid);
n += fake_pid_len + 2;
}
if (show_all)
printf(": %.*s\n", (int) message_len, message);
else if (contains_unprintable(message, message_len)) {
char bytes[FORMAT_BYTES_MAX];
printf(": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len));
} else if (message_len + n < n_columns)
printf(": %.*s\n", (int) message_len, message);
else if (n < n_columns) {
char *e;
e = ellipsize_mem(message, message_len, n_columns - n - 2, 90);
if (!e)
printf(": %.*s\n", (int) message_len, message);
else
printf(": %s\n", e);
free(e);
} else
fputs("\n", stdout);
r = 0;
finish:
free(hostname);
free(identifier);
free(comm);
free(pid);
free(fake_pid);
free(message);
free(monotonic);
free(realtime);
return r;
}
static int output_short_realtime(sd_journal *j, unsigned line, unsigned n_columns, bool show_all) {
return output_short(j, line, n_columns, show_all, false);
}
static int output_short_monotonic(sd_journal *j, unsigned line, unsigned n_columns, bool show_all) {
return output_short(j, line, n_columns, show_all, true);
}
static int output_verbose(sd_journal *j, unsigned line, unsigned n_columns, bool show_all) {
const void *data;
size_t length;
char *cursor;
uint64_t realtime;
char ts[FORMAT_TIMESTAMP_MAX];
int r;
assert(j);
r = sd_journal_get_realtime_usec(j, &realtime);
if (r < 0) {
log_error("Failed to get realtime timestamp: %s", strerror(-r));
return r;
}
r = sd_journal_get_cursor(j, &cursor);
if (r < 0) {
log_error("Failed to get cursor: %s", strerror(-r));
return r;
}
printf("%s [%s]\n",
format_timestamp(ts, sizeof(ts), realtime),
cursor);
free(cursor);
SD_JOURNAL_FOREACH_DATA(j, data, length) {
if (!show_all && (length > PRINT_THRESHOLD ||
contains_unprintable(data, length))) {
const char *c;
char bytes[FORMAT_BYTES_MAX];
c = memchr(data, '=', length);
if (!c) {
log_error("Invalid field.");
return -EINVAL;
}
printf("\t%.*s=[%s blob data]\n",
(int) (c - (const char*) data),
(const char*) data,
format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1));
} else
printf("\t%.*s\n", (int) length, (const char*) data);
}
return 0;
}
static int output_export(sd_journal *j, unsigned line, unsigned n_columns, bool show_all) {
sd_id128_t boot_id;
char sid[33];
int r;
usec_t realtime, monotonic;
char *cursor;
const void *data;
size_t length;
assert(j);
r = sd_journal_get_realtime_usec(j, &realtime);
if (r < 0) {
log_error("Failed to get realtime timestamp: %s", strerror(-r));
return r;
}
r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
if (r < 0) {
log_error("Failed to get monotonic timestamp: %s", strerror(-r));
return r;
}
r = sd_journal_get_cursor(j, &cursor);
if (r < 0) {
log_error("Failed to get cursor: %s", strerror(-r));
return r;
}
printf("__CURSOR=%s\n"
"__REALTIME_TIMESTAMP=%llu\n"
"__MONOTONIC_TIMESTAMP=%llu\n"
"_BOOT_ID=%s\n",
cursor,
(unsigned long long) realtime,
(unsigned long long) monotonic,
sd_id128_to_string(boot_id, sid));
free(cursor);
SD_JOURNAL_FOREACH_DATA(j, data, length) {
/* We already printed the boot id, from the data in
* the header, hence let's suppress it here */
if (length >= 9 &&
memcmp(data, "_BOOT_ID=", 9) == 0)
continue;
if (contains_unprintable(data, length)) {
const char *c;
uint64_t le64;
c = memchr(data, '=', length);
if (!c) {
log_error("Invalid field.");
return -EINVAL;
}
fwrite(data, c - (const char*) data, 1, stdout);
fputc('\n', stdout);
le64 = htole64(length - (c - (const char*) data) - 1);
fwrite(&le64, sizeof(le64), 1, stdout);
fwrite(c + 1, length - (c - (const char*) data) - 1, 1, stdout);
} else
fwrite(data, length, 1, stdout);
fputc('\n', stdout);
}
fputc('\n', stdout);
return 0;
}
static void json_escape(const char* p, size_t l) {
if (contains_unprintable(p, l)) {
bool not_first = false;
fputs("[ ", stdout);
while (l > 0) {
if (not_first)
printf(", %u", (uint8_t) *p);
else {
not_first = true;
printf("%u", (uint8_t) *p);
}
p++;
l--;
}
fputs(" ]", stdout);
} else {
fputc('\"', stdout);
while (l > 0) {
if (*p == '"' || *p == '\\') {
fputc('\\', stdout);
fputc(*p, stdout);
} else
fputc(*p, stdout);
p++;
l--;
}
fputc('\"', stdout);
}
}
static int output_json(sd_journal *j, unsigned line, unsigned n_columns, bool show_all) {
uint64_t realtime, monotonic;
char *cursor;
const void *data;
size_t length;
sd_id128_t boot_id;
char sid[33];
int r;
assert(j);
r = sd_journal_get_realtime_usec(j, &realtime);
if (r < 0) {
log_error("Failed to get realtime timestamp: %s", strerror(-r));
return r;
}
r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
if (r < 0) {
log_error("Failed to get monotonic timestamp: %s", strerror(-r));
return r;
}
r = sd_journal_get_cursor(j, &cursor);
if (r < 0) {
log_error("Failed to get cursor: %s", strerror(-r));
return r;
}
if (line == 1)
fputc('\n', stdout);
else
fputs(",\n", stdout);
printf("{\n"
"\t\"__CURSOR\" : \"%s\",\n"
"\t\"__REALTIME_TIMESTAMP\" : \"%llu\",\n"
"\t\"__MONOTONIC_TIMESTAMP\" : \"%llu\",\n"
"\t\"_BOOT_ID\" : \"%s\"",
cursor,
(unsigned long long) realtime,
(unsigned long long) monotonic,
sd_id128_to_string(boot_id, sid));
free(cursor);
SD_JOURNAL_FOREACH_DATA(j, data, length) {
const char *c;
/* We already printed the boot id, from the data in
* the header, hence let's suppress it here */
if (length >= 9 &&
memcmp(data, "_BOOT_ID=", 9) == 0)
continue;
c = memchr(data, '=', length);
if (!c) {
log_error("Invalid field.");
return -EINVAL;
}
fputs(",\n\t", stdout);
json_escape(data, c - (const char*) data);
fputs(" : ", stdout);
json_escape(c + 1, length - (c - (const char*) data) - 1);
}
fputs("\n}", stdout);
fflush(stdout);
return 0;
}
static int output_cat(sd_journal *j, unsigned line, unsigned n_columns, bool show_all) {
const void *data;
size_t l;
int r;
assert(j);
r = sd_journal_get_data(j, "MESSAGE", &data, &l);
if (r < 0) {
log_error("Failed to get data: %s", strerror(-r));
return r;
}
assert(l >= 8);
fwrite((const char*) data + 8, 1, l - 8, stdout);
putchar('\n');
return 0;
}
static int (*output_funcs[_OUTPUT_MODE_MAX])(sd_journal*j, unsigned line, unsigned n_columns, bool show_all) = {
[OUTPUT_SHORT] = output_short_realtime,
[OUTPUT_SHORT_MONOTONIC] = output_short_monotonic,
[OUTPUT_VERBOSE] = output_verbose,
[OUTPUT_EXPORT] = output_export,
[OUTPUT_JSON] = output_json,
[OUTPUT_CAT] = output_cat
};
int output_journal(sd_journal *j, OutputMode mode, unsigned line, unsigned n_columns, bool show_all) {
assert(mode >= 0);
assert(mode < _OUTPUT_MODE_MAX);
if (n_columns <= 0)
n_columns = columns();
return output_funcs[mode](j, line, n_columns, show_all);
}
int show_journal_by_unit(
const char *unit,
OutputMode mode,
unsigned n_columns,
usec_t not_before,
unsigned how_many,
bool show_all,
bool follow) {
char *m = NULL;
sd_journal *j;
int r;
int fd;
unsigned line = 0;
bool need_seek = false;
assert(mode >= 0);
assert(mode < _OUTPUT_MODE_MAX);
assert(unit);
if (!endswith(unit, ".service") &&
!endswith(unit, ".socket") &&
!endswith(unit, ".mount") &&
!endswith(unit, ".swap"))
return 0;
if (how_many <= 0)
return 0;
if (asprintf(&m, "_SYSTEMD_UNIT=%s", unit) < 0) {
r = -ENOMEM;
goto finish;
}
r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM_ONLY);
if (r < 0)
goto finish;
fd = sd_journal_get_fd(j);
if (fd < 0)
goto finish;
r = sd_journal_add_match(j, m, strlen(m));
if (r < 0)
goto finish;
r = sd_journal_seek_tail(j);
if (r < 0)
goto finish;
r = sd_journal_previous_skip(j, how_many);
if (r < 0)
goto finish;
if (mode == OUTPUT_JSON) {
fputc('[', stdout);
fflush(stdout);
}
for (;;) {
for (;;) {
usec_t usec;
if (need_seek) {
r = sd_journal_next(j);
if (r < 0)
goto finish;
}
if (r == 0)
break;
need_seek = true;
if (not_before > 0) {
r = sd_journal_get_monotonic_usec(j, &usec, NULL);
/* -ESTALE is returned if the
timestamp is not from this boot */
if (r == -ESTALE)
continue;
else if (r < 0)
goto finish;
if (usec < not_before)
continue;
}
line ++;
r = output_journal(j, mode, line, n_columns, show_all);
if (r < 0)
goto finish;
}
if (!follow)
break;
r = fd_wait_for_event(fd, POLLIN, (usec_t) -1);
if (r < 0)
goto finish;
r = sd_journal_process(j);
if (r < 0)
goto finish;
}
if (mode == OUTPUT_JSON)
fputs("\n]\n", stdout);
finish:
if (m)
free(m);
if (j)
sd_journal_close(j);
return r;
}
static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
[OUTPUT_SHORT] = "short",
[OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
[OUTPUT_VERBOSE] = "verbose",
[OUTPUT_EXPORT] = "export",
[OUTPUT_JSON] = "json",
[OUTPUT_CAT] = "cat"
};
DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode);
+56
View File
@@ -0,0 +1,56 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#ifndef foologsshowhfoo
#define foologsshowhfoo
/***
This file is part of systemd.
Copyright 2012 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdbool.h>
#include <systemd/sd-journal.h>
#include "util.h"
typedef enum OutputMode {
OUTPUT_SHORT,
OUTPUT_SHORT_MONOTONIC,
OUTPUT_VERBOSE,
OUTPUT_EXPORT,
OUTPUT_JSON,
OUTPUT_CAT,
_OUTPUT_MODE_MAX,
_OUTPUT_MODE_INVALID = -1
} OutputMode;
int output_journal(sd_journal *j, OutputMode mode, unsigned line, unsigned n_columns, bool show_all);
int show_journal_by_unit(
const char *unit,
OutputMode mode,
unsigned n_columns,
usec_t not_before,
unsigned how_many,
bool show_all,
bool follow);
const char* output_mode_to_string(OutputMode m);
OutputMode output_mode_from_string(const char *s);
#endif
+274
View File
@@ -0,0 +1,274 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <errno.h>
#include <sys/socket.h>
#include <net/if.h>
#include <asm/types.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include "util.h"
#include "macro.h"
#include "loopback-setup.h"
#include "socket-util.h"
#define NLMSG_TAIL(nmsg) \
((struct rtattr *) (((uint8_t*) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
static int add_rtattr(struct nlmsghdr *n, size_t max_length, int type, const void *data, size_t data_length) {
size_t length;
struct rtattr *rta;
length = RTA_LENGTH(data_length);
if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(length) > max_length)
return -E2BIG;
rta = NLMSG_TAIL(n);
rta->rta_type = type;
rta->rta_len = length;
memcpy(RTA_DATA(rta), data, data_length);
n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(length);
return 0;
}
static ssize_t sendto_loop(int fd, const void *buf, size_t buf_len, int flags, const struct sockaddr *sa, socklen_t sa_len) {
for (;;) {
ssize_t l;
if ((l = sendto(fd, buf, buf_len, flags, sa, sa_len)) >= 0)
return l;
if (errno != EINTR)
return -errno;
}
}
static ssize_t recvfrom_loop(int fd, void *buf, size_t buf_len, int flags, struct sockaddr *sa, socklen_t *sa_len) {
for (;;) {
ssize_t l;
if ((l = recvfrom(fd, buf, buf_len, flags, sa, sa_len)) >= 0)
return l;
if (errno != EINTR)
return -errno;
}
}
static int add_adresses(int fd, int if_loopback, unsigned *requests) {
union {
struct sockaddr sa;
struct sockaddr_nl nl;
} sa;
union {
struct nlmsghdr header;
uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
NLMSG_ALIGN(sizeof(struct ifaddrmsg)) +
RTA_LENGTH(sizeof(struct in6_addr))];
} request;
struct ifaddrmsg *ifaddrmsg;
uint32_t ipv4_address = htonl(INADDR_LOOPBACK);
int r;
zero(request);
request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
request.header.nlmsg_type = RTM_NEWADDR;
request.header.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_ACK;
request.header.nlmsg_seq = *requests + 1;
ifaddrmsg = NLMSG_DATA(&request.header);
ifaddrmsg->ifa_family = AF_INET;
ifaddrmsg->ifa_prefixlen = 8;
ifaddrmsg->ifa_flags = IFA_F_PERMANENT;
ifaddrmsg->ifa_scope = RT_SCOPE_HOST;
ifaddrmsg->ifa_index = if_loopback;
if ((r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &ipv4_address, sizeof(ipv4_address))) < 0)
return r;
zero(sa);
sa.nl.nl_family = AF_NETLINK;
if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
return -errno;
(*requests)++;
if (!socket_ipv6_is_supported())
return 0;
request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
request.header.nlmsg_seq = *requests + 1;
ifaddrmsg->ifa_family = AF_INET6;
ifaddrmsg->ifa_prefixlen = 128;
if ((r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &in6addr_loopback, sizeof(in6addr_loopback))) < 0)
return r;
if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
return -errno;
(*requests)++;
return 0;
}
static int start_interface(int fd, int if_loopback, unsigned *requests) {
union {
struct sockaddr sa;
struct sockaddr_nl nl;
} sa;
union {
struct nlmsghdr header;
uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
NLMSG_ALIGN(sizeof(struct ifinfomsg))];
} request;
struct ifinfomsg *ifinfomsg;
zero(request);
request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
request.header.nlmsg_type = RTM_NEWLINK;
request.header.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
request.header.nlmsg_seq = *requests + 1;
ifinfomsg = NLMSG_DATA(&request.header);
ifinfomsg->ifi_family = AF_UNSPEC;
ifinfomsg->ifi_index = if_loopback;
ifinfomsg->ifi_flags = IFF_UP;
ifinfomsg->ifi_change = IFF_UP;
zero(sa);
sa.nl.nl_family = AF_NETLINK;
if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
return -errno;
(*requests)++;
return 0;
}
static int read_response(int fd, unsigned requests_max) {
union {
struct sockaddr sa;
struct sockaddr_nl nl;
} sa;
union {
struct nlmsghdr header;
uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
NLMSG_ALIGN(sizeof(struct nlmsgerr))];
} response;
ssize_t l;
socklen_t sa_len = sizeof(sa);
struct nlmsgerr *nlmsgerr;
if ((l = recvfrom_loop(fd, &response, sizeof(response), 0, &sa.sa, &sa_len)) < 0)
return -errno;
if (sa_len != sizeof(sa.nl) ||
sa.nl.nl_family != AF_NETLINK)
return -EIO;
if (sa.nl.nl_pid != 0)
return 0;
if ((size_t) l < sizeof(struct nlmsghdr))
return -EIO;
if (response.header.nlmsg_type != NLMSG_ERROR ||
(pid_t) response.header.nlmsg_pid != getpid() ||
response.header.nlmsg_seq >= requests_max)
return 0;
if ((size_t) l < NLMSG_LENGTH(sizeof(struct nlmsgerr)) ||
response.header.nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
return -EIO;
nlmsgerr = NLMSG_DATA(&response.header);
if (nlmsgerr->error < 0 && nlmsgerr->error != -EEXIST) {
log_warning("Netlink failure for request %i: %s", response.header.nlmsg_seq, strerror(-nlmsgerr->error));
return nlmsgerr->error;
}
return response.header.nlmsg_seq;
}
int loopback_setup(void) {
int r, if_loopback;
union {
struct sockaddr sa;
struct sockaddr_nl nl;
struct sockaddr_storage storage;
} sa;
unsigned requests = 0, i;
int fd;
errno = 0;
if ((if_loopback = (int) if_nametoindex("lo")) <= 0)
return errno ? -errno : -ENODEV;
if ((fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0)
return -errno;
zero(sa);
sa.nl.nl_family = AF_NETLINK;
if (bind(fd, &sa.sa, sizeof(sa)) < 0) {
r = -errno;
goto finish;
}
if ((r = add_adresses(fd, if_loopback, &requests)) < 0)
goto finish;
if ((r = start_interface(fd, if_loopback, &requests)) < 0)
goto finish;
for (i = 0; i < requests; i++) {
if ((r = read_response(fd, requests)) < 0)
goto finish;
}
r = 0;
finish:
if (r < 0)
log_warning("Failed to configure loopback device: %s", strerror(-r));
if (fd >= 0)
close_nointr_nofail(fd);
return r;
}
+27
View File
@@ -0,0 +1,27 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#ifndef fooloopbacksetuphfoo
#define fooloopbacksetuphfoo
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
int loopback_setup(void);
#endif
+265
View File
@@ -0,0 +1,265 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mount.h>
#include <systemd/sd-id128.h>
#include "machine-id-setup.h"
#include "macro.h"
#include "util.h"
#include "mkdir.h"
#include "log.h"
#include "virt.h"
static int shorten_uuid(char destination[36], const char *source) {
unsigned i, j;
for (i = 0, j = 0; i < 36 && j < 32; i++) {
int t;
t = unhexchar(source[i]);
if (t < 0)
continue;
destination[j++] = hexchar(t);
}
if (i == 36 && j == 32) {
destination[32] = '\n';
destination[33] = 0;
return 0;
}
return -EINVAL;
}
static int generate(char id[34]) {
int fd, r;
unsigned char *p;
sd_id128_t buf;
char *q;
ssize_t k;
const char *vm_id;
assert(id);
/* First, try reading the D-Bus machine id, unless it is a symlink */
fd = open("/var/lib/dbus/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
if (fd >= 0) {
k = loop_read(fd, id, 32, false);
close_nointr_nofail(fd);
if (k >= 32) {
id[32] = '\n';
id[33] = 0;
log_info("Initializing machine ID from D-Bus machine ID.");
return 0;
}
}
/* If that didn't work, see if we are running in qemu/kvm and a
* machine ID was passed in via -uuid on the qemu/kvm command
* line */
r = detect_vm(&vm_id);
if (r > 0 && streq(vm_id, "kvm")) {
char uuid[37];
fd = open("/sys/class/dmi/id/product_uuid", O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
if (fd >= 0) {
k = loop_read(fd, uuid, 36, false);
close_nointr_nofail(fd);
if (k >= 36) {
r = shorten_uuid(id, uuid);
if (r >= 0) {
log_info("Initializing machine ID from KVM UUID");
return 0;
}
}
}
}
/* If that didn't work either, see if we are running in a
* container, and a machine ID was passed in via
* $container_uuid the way libvirt/LXC does it */
r = detect_container(NULL);
if (r > 0) {
FILE *f;
f = fopen("/proc/1/environ", "re");
if (f) {
bool done = false;
do {
char line[LINE_MAX];
unsigned i;
for (i = 0; i < sizeof(line)-1; i++) {
int c;
c = getc(f);
if (_unlikely_(c == EOF)) {
done = true;
break;
} else if (c == 0)
break;
line[i] = c;
}
line[i] = 0;
if (startswith(line, "container_uuid=") &&
strlen(line + 15) >= 36) {
r = shorten_uuid(id, line + 15);
if (r >= 0) {
log_info("Initializing machine ID from container UUID");
return 0;
}
}
} while (!done);
fclose(f);
}
}
/* If that didn't work, generate a random machine id */
r = sd_id128_randomize(&buf);
if (r < 0) {
log_error("Failed to open /dev/urandom: %s", strerror(-r));
return r;
}
for (p = buf.bytes, q = id; p < buf.bytes + sizeof(buf); p++, q += 2) {
q[0] = hexchar(*p >> 4);
q[1] = hexchar(*p & 15);
}
id[32] = '\n';
id[33] = 0;
log_info("Initializing machine ID from random generator.");
return 0;
}
int machine_id_setup(void) {
int fd, r;
bool writable;
struct stat st;
char id[34]; /* 32 + \n + \0 */
mode_t m;
m = umask(0000);
/* We create this 0444, to indicate that this isn't really
* something you should ever modify. Of course, since the file
* will be owned by root it doesn't matter much, but maybe
* people look. */
fd = open("/etc/machine-id", O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY, 0444);
if (fd >= 0)
writable = true;
else {
fd = open("/etc/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0) {
umask(m);
log_error("Cannot open /etc/machine-id: %m");
return -errno;
}
writable = false;
}
umask(m);
if (fstat(fd, &st) < 0) {
log_error("fstat() failed: %m");
r = -errno;
goto finish;
}
if (S_ISREG(st.st_mode)) {
if (loop_read(fd, id, 32, false) >= 32) {
r = 0;
goto finish;
}
}
/* Hmm, so, the id currently stored is not useful, then let's
* generate one */
r = generate(id);
if (r < 0)
goto finish;
if (S_ISREG(st.st_mode) && writable) {
lseek(fd, 0, SEEK_SET);
if (loop_write(fd, id, 33, false) == 33) {
r = 0;
goto finish;
}
}
close_nointr_nofail(fd);
fd = -1;
/* Hmm, we couldn't write it? So let's write it to
* /run/machine-id as a replacement */
m = umask(0022);
r = write_one_line_file("/run/machine-id", id);
umask(m);
if (r < 0) {
log_error("Cannot write /run/machine-id: %s", strerror(-r));
unlink("/run/machine-id");
goto finish;
}
/* And now, let's mount it over */
r = mount("/run/machine-id", "/etc/machine-id", "bind", MS_BIND|MS_RDONLY, NULL) < 0 ? -errno : 0;
if (r < 0) {
unlink("/run/machine-id");
log_error("Failed to mount /etc/machine-id: %s", strerror(-r));
} else
log_info("Installed transient /etc/machine-id file.");
finish:
if (fd >= 0)
close_nointr_nofail(fd);
return r;
}
+27
View File
@@ -0,0 +1,27 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#ifndef foomachineidsetuphfoo
#define foomachineidsetuphfoo
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
int machine_id_setup(void);
#endif
+423
View File
@@ -0,0 +1,423 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <sys/mount.h>
#include <errno.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h>
#include <assert.h>
#include <unistd.h>
#include <ftw.h>
#include "mount-setup.h"
#include "log.h"
#include "macro.h"
#include "util.h"
#include "label.h"
#include "set.h"
#include "strv.h"
#include "mkdir.h"
#ifndef TTY_GID
#define TTY_GID 5
#endif
typedef struct MountPoint {
const char *what;
const char *where;
const char *type;
const char *options;
unsigned long flags;
bool fatal;
} MountPoint;
/* The first three entries we might need before SELinux is up. The
* fourth (securityfs) is needed by IMA to load a custom policy. The
* other ones we can delay until SELinux and IMA are loaded. */
#define N_EARLY_MOUNT 4
static const MountPoint mount_table[] = {
{ "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true },
{ "sysfs", "/sys", "sysfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true },
{ "devtmpfs", "/dev", "devtmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, true },
{ "securityfs", "/sys/kernel/security", "securityfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, false },
{ "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true },
{ "devpts", "/dev/pts", "devpts", "mode=620,gid=" STRINGIFY(TTY_GID), MS_NOSUID|MS_NOEXEC, false },
{ "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true },
{ "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, false },
{ "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV, false },
};
/* These are API file systems that might be mounted by other software,
* we just list them here so that we know that we should ignore them */
static const char * const ignore_paths[] = {
"/sys/fs/selinux",
"/selinux",
"/proc/bus/usb"
};
bool mount_point_is_api(const char *path) {
unsigned i;
/* Checks if this mount point is considered "API", and hence
* should be ignored */
for (i = 0; i < ELEMENTSOF(mount_table); i ++)
if (path_equal(path, mount_table[i].where))
return true;
return path_startswith(path, "/sys/fs/cgroup/");
}
bool mount_point_ignore(const char *path) {
unsigned i;
for (i = 0; i < ELEMENTSOF(ignore_paths); i++)
if (path_equal(path, ignore_paths[i]))
return true;
return false;
}
static int mount_one(const MountPoint *p, bool relabel) {
int r;
assert(p);
/* Relabel first, just in case */
if (relabel)
label_fix(p->where, true);
if ((r = path_is_mount_point(p->where, true)) < 0)
return r;
if (r > 0)
return 0;
/* The access mode here doesn't really matter too much, since
* the mounted file system will take precedence anyway. */
mkdir_p(p->where, 0755);
log_debug("Mounting %s to %s of type %s with options %s.",
p->what,
p->where,
p->type,
strna(p->options));
if (mount(p->what,
p->where,
p->type,
p->flags,
p->options) < 0) {
log_error("Failed to mount %s: %s", p->where, strerror(errno));
return p->fatal ? -errno : 0;
}
/* Relabel again, since we now mounted something fresh here */
if (relabel)
label_fix(p->where, false);
return 1;
}
int mount_setup_early(void) {
unsigned i;
int r = 0;
assert_cc(N_EARLY_MOUNT <= ELEMENTSOF(mount_table));
/* Do a minimal mount of /proc and friends to enable the most
* basic stuff, such as SELinux */
for (i = 0; i < N_EARLY_MOUNT; i ++) {
int j;
j = mount_one(mount_table + i, false);
if (r == 0)
r = j;
}
return r;
}
int mount_cgroup_controllers(char ***join_controllers) {
int r;
FILE *f;
char buf[LINE_MAX];
Set *controllers;
/* Mount all available cgroup controllers that are built into the kernel. */
f = fopen("/proc/cgroups", "re");
if (!f) {
log_error("Failed to enumerate cgroup controllers: %m");
return 0;
}
controllers = set_new(string_hash_func, string_compare_func);
if (!controllers) {
r = -ENOMEM;
log_error("Failed to allocate controller set.");
goto finish;
}
/* Ignore the header line */
(void) fgets(buf, sizeof(buf), f);
for (;;) {
char *controller;
int enabled = 0;
if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
if (feof(f))
break;
log_error("Failed to parse /proc/cgroups.");
r = -EIO;
goto finish;
}
if (!enabled) {
free(controller);
continue;
}
r = set_put(controllers, controller);
if (r < 0) {
log_error("Failed to add controller to set.");
free(controller);
goto finish;
}
}
for (;;) {
MountPoint p;
char *controller, *where, *options;
char ***k = NULL;
controller = set_steal_first(controllers);
if (!controller)
break;
if (join_controllers)
for (k = join_controllers; *k; k++)
if (strv_find(*k, controller))
break;
if (k && *k) {
char **i, **j;
for (i = *k, j = *k; *i; i++) {
if (!streq(*i, controller)) {
char *t;
t = set_remove(controllers, *i);
if (!t) {
free(*i);
continue;
}
free(t);
}
*(j++) = *i;
}
*j = NULL;
options = strv_join(*k, ",");
if (!options) {
log_error("Failed to join options");
free(controller);
r = -ENOMEM;
goto finish;
}
} else {
options = controller;
controller = NULL;
}
where = strappend("/sys/fs/cgroup/", options);
if (!where) {
log_error("Failed to build path");
free(options);
r = -ENOMEM;
goto finish;
}
zero(p);
p.what = "cgroup";
p.where = where;
p.type = "cgroup";
p.options = options;
p.flags = MS_NOSUID|MS_NOEXEC|MS_NODEV;
p.fatal = false;
r = mount_one(&p, true);
free(controller);
free(where);
if (r < 0) {
free(options);
goto finish;
}
if (r > 0 && k && *k) {
char **i;
for (i = *k; *i; i++) {
char *t;
t = strappend("/sys/fs/cgroup/", *i);
if (!t) {
log_error("Failed to build path");
r = -ENOMEM;
free(options);
goto finish;
}
r = symlink(options, t);
free(t);
if (r < 0 && errno != EEXIST) {
log_error("Failed to create symlink: %m");
r = -errno;
free(options);
goto finish;
}
}
}
free(options);
}
r = 0;
finish:
set_free_free(controllers);
fclose(f);
return r;
}
static int symlink_and_label(const char *old_path, const char *new_path) {
int r;
assert(old_path);
assert(new_path);
if ((r = label_symlinkfile_set(new_path)) < 0)
return r;
if (symlink(old_path, new_path) < 0)
r = -errno;
label_file_clear();
return r;
}
static int nftw_cb(
const char *fpath,
const struct stat *sb,
int tflag,
struct FTW *ftwbuf) {
/* No need to label /dev twice in a row... */
if (_unlikely_(ftwbuf->level == 0))
return FTW_CONTINUE;
label_fix(fpath, true);
/* /run/initramfs is static data and big, no need to
* dynamically relabel its contents at boot... */
if (_unlikely_(ftwbuf->level == 1 &&
tflag == FTW_D &&
streq(fpath, "/run/initramfs")))
return FTW_SKIP_SUBTREE;
return FTW_CONTINUE;
};
int mount_setup(bool loaded_policy) {
static const char symlinks[] =
"/proc/kcore\0" "/dev/core\0"
"/proc/self/fd\0" "/dev/fd\0"
"/proc/self/fd/0\0" "/dev/stdin\0"
"/proc/self/fd/1\0" "/dev/stdout\0"
"/proc/self/fd/2\0" "/dev/stderr\0";
static const char relabel[] =
"/run/initramfs/root-fsck\0"
"/run/initramfs/shutdown\0";
int r;
unsigned i;
const char *j, *k;
for (i = 0; i < ELEMENTSOF(mount_table); i ++) {
r = mount_one(mount_table + i, true);
if (r < 0)
return r;
}
/* Nodes in devtmpfs and /run need to be manually updated for
* the appropriate labels, after mounting. The other virtual
* API file systems like /sys and /proc do not need that, they
* use the same label for all their files. */
if (loaded_policy) {
usec_t before_relabel, after_relabel;
char timespan[FORMAT_TIMESPAN_MAX];
before_relabel = now(CLOCK_MONOTONIC);
nftw("/dev", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
nftw("/run", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
/* Explicitly relabel these */
NULSTR_FOREACH(j, relabel)
label_fix(j, true);
after_relabel = now(CLOCK_MONOTONIC);
log_info("Relabelled /dev and /run in %s.",
format_timespan(timespan, sizeof(timespan), after_relabel - before_relabel));
}
/* Create a few default symlinks, which are normally created
* by udevd, but some scripts might need them before we start
* udevd. */
NULSTR_FOREACH_PAIR(j, k, symlinks)
symlink_and_label(j, k);
/* Create a few directories we always want around */
label_mkdir("/run/systemd", 0755);
label_mkdir("/run/systemd/system", 0755);
return 0;
}
+36
View File
@@ -0,0 +1,36 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#ifndef foomountsetuphfoo
#define foomountsetuphfoo
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdbool.h>
int mount_setup_early(void);
int mount_setup(bool loaded_policy);
int mount_cgroup_controllers(char ***join_controllers);
bool mount_point_is_api(const char *path);
bool mount_point_ignore(const char *path);
#endif
+348
View File
@@ -0,0 +1,348 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include "util.h"
#include "mkdir.h"
#include "strv.h"
#include "path-lookup.h"
int user_config_home(char **config_home) {
const char *e;
if ((e = getenv("XDG_CONFIG_HOME"))) {
if (asprintf(config_home, "%s/systemd/user", e) < 0)
return -ENOMEM;
return 1;
} else {
const char *home;
if ((home = getenv("HOME"))) {
if (asprintf(config_home, "%s/.config/systemd/user", home) < 0)
return -ENOMEM;
return 1;
}
}
return 0;
}
static char** user_dirs(void) {
const char * const config_unit_paths[] = {
USER_CONFIG_UNIT_PATH,
"/etc/systemd/user",
"/run/systemd/user",
NULL
};
const char * const data_unit_paths[] = {
"/usr/local/lib/systemd/user",
"/usr/local/share/systemd/user",
USER_DATA_UNIT_PATH,
"/usr/lib/systemd/user",
"/usr/share/systemd/user",
NULL
};
const char *home, *e;
char *config_home = NULL, *data_home = NULL;
char **config_dirs = NULL, **data_dirs = NULL;
char **r = NULL, **t;
/* Implement the mechanisms defined in
*
* http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
*
* We look in both the config and the data dirs because we
* want to encourage that distributors ship their unit files
* as data, and allow overriding as configuration.
*/
if (user_config_home(&config_home) < 0)
goto fail;
home = getenv("HOME");
if ((e = getenv("XDG_CONFIG_DIRS")))
if (!(config_dirs = strv_split(e, ":")))
goto fail;
/* We don't treat /etc/xdg/systemd here as the spec
* suggests because we assume that that is a link to
* /etc/systemd/ anyway. */
if ((e = getenv("XDG_DATA_HOME"))) {
if (asprintf(&data_home, "%s/systemd/user", e) < 0)
goto fail;
} else if (home) {
if (asprintf(&data_home, "%s/.local/share/systemd/user", home) < 0)
goto fail;
/* There is really no need for two unit dirs in $HOME,
* except to be fully compliant with the XDG spec. We
* now try to link the two dirs, so that we can
* minimize disk seeks a little. Further down we'll
* then filter out this link, if it is actually is
* one. */
mkdir_parents(data_home, 0777);
(void) symlink("../../../.config/systemd/user", data_home);
}
if ((e = getenv("XDG_DATA_DIRS")))
data_dirs = strv_split(e, ":");
else
data_dirs = strv_new("/usr/local/share",
"/usr/share",
NULL);
if (!data_dirs)
goto fail;
/* Now merge everything we found. */
if (config_home) {
if (!(t = strv_append(r, config_home)))
goto fail;
strv_free(r);
r = t;
}
if (!strv_isempty(config_dirs)) {
if (!(t = strv_merge_concat(r, config_dirs, "/systemd/user")))
goto finish;
strv_free(r);
r = t;
}
if (!(t = strv_merge(r, (char**) config_unit_paths)))
goto fail;
strv_free(r);
r = t;
if (data_home) {
if (!(t = strv_append(r, data_home)))
goto fail;
strv_free(r);
r = t;
}
if (!strv_isempty(data_dirs)) {
if (!(t = strv_merge_concat(r, data_dirs, "/systemd/user")))
goto fail;
strv_free(r);
r = t;
}
if (!(t = strv_merge(r, (char**) data_unit_paths)))
goto fail;
strv_free(r);
r = t;
if (!strv_path_make_absolute_cwd(r))
goto fail;
finish:
free(config_home);
strv_free(config_dirs);
free(data_home);
strv_free(data_dirs);
return r;
fail:
strv_free(r);
r = NULL;
goto finish;
}
int lookup_paths_init(LookupPaths *p, ManagerRunningAs running_as, bool personal) {
const char *e;
char *t;
assert(p);
/* First priority is whatever has been passed to us via env
* vars */
if ((e = getenv("SYSTEMD_UNIT_PATH")))
if (!(p->unit_path = split_path_and_make_absolute(e)))
return -ENOMEM;
if (strv_isempty(p->unit_path)) {
/* Nothing is set, so let's figure something out. */
strv_free(p->unit_path);
if (running_as == MANAGER_USER) {
if (personal)
p->unit_path = user_dirs();
else
p->unit_path = strv_new(
/* If you modify this you also want to modify
* systemduserunitpath= in systemd.pc.in, and
* the arrays in user_dirs() above! */
USER_CONFIG_UNIT_PATH,
"/etc/systemd/user",
"/run/systemd/user",
"/usr/local/lib/systemd/user",
"/usr/local/share/systemd/user",
USER_DATA_UNIT_PATH,
"/usr/lib/systemd/user",
"/usr/share/systemd/user",
NULL);
if (!p->unit_path)
return -ENOMEM;
} else
if (!(p->unit_path = strv_new(
/* If you modify this you also want to modify
* systemdsystemunitpath= in systemd.pc.in! */
SYSTEM_CONFIG_UNIT_PATH,
"/etc/systemd/system",
"/run/systemd/system",
"/usr/local/lib/systemd/system",
SYSTEM_DATA_UNIT_PATH,
"/usr/lib/systemd/system",
#ifdef HAVE_SPLIT_USR
"/lib/systemd/system",
#endif
NULL)))
return -ENOMEM;
}
if (p->unit_path)
if (!strv_path_canonicalize(p->unit_path))
return -ENOMEM;
strv_uniq(p->unit_path);
strv_path_remove_empty(p->unit_path);
if (!strv_isempty(p->unit_path)) {
if (!(t = strv_join(p->unit_path, "\n\t")))
return -ENOMEM;
log_debug("Looking for unit files in:\n\t%s", t);
free(t);
} else {
log_debug("Ignoring unit files.");
strv_free(p->unit_path);
p->unit_path = NULL;
}
if (running_as == MANAGER_SYSTEM) {
#ifdef HAVE_SYSV_COMPAT
/* /etc/init.d/ compatibility does not matter to users */
if ((e = getenv("SYSTEMD_SYSVINIT_PATH")))
if (!(p->sysvinit_path = split_path_and_make_absolute(e)))
return -ENOMEM;
if (strv_isempty(p->sysvinit_path)) {
strv_free(p->sysvinit_path);
if (!(p->sysvinit_path = strv_new(
SYSTEM_SYSVINIT_PATH, /* /etc/init.d/ */
NULL)))
return -ENOMEM;
}
if ((e = getenv("SYSTEMD_SYSVRCND_PATH")))
if (!(p->sysvrcnd_path = split_path_and_make_absolute(e)))
return -ENOMEM;
if (strv_isempty(p->sysvrcnd_path)) {
strv_free(p->sysvrcnd_path);
if (!(p->sysvrcnd_path = strv_new(
SYSTEM_SYSVRCND_PATH, /* /etc/rcN.d/ */
NULL)))
return -ENOMEM;
}
if (p->sysvinit_path)
if (!strv_path_canonicalize(p->sysvinit_path))
return -ENOMEM;
if (p->sysvrcnd_path)
if (!strv_path_canonicalize(p->sysvrcnd_path))
return -ENOMEM;
strv_uniq(p->sysvinit_path);
strv_uniq(p->sysvrcnd_path);
strv_path_remove_empty(p->sysvinit_path);
strv_path_remove_empty(p->sysvrcnd_path);
if (!strv_isempty(p->sysvinit_path)) {
if (!(t = strv_join(p->sysvinit_path, "\n\t")))
return -ENOMEM;
log_debug("Looking for SysV init scripts in:\n\t%s", t);
free(t);
} else {
log_debug("Ignoring SysV init scripts.");
strv_free(p->sysvinit_path);
p->sysvinit_path = NULL;
}
if (!strv_isempty(p->sysvrcnd_path)) {
if (!(t = strv_join(p->sysvrcnd_path, "\n\t")))
return -ENOMEM;
log_debug("Looking for SysV rcN.d links in:\n\t%s", t);
free(t);
} else {
log_debug("Ignoring SysV rcN.d links.");
strv_free(p->sysvrcnd_path);
p->sysvrcnd_path = NULL;
}
#else
log_debug("Disabled SysV init scripts and rcN.d links support");
#endif
}
return 0;
}
void lookup_paths_free(LookupPaths *p) {
assert(p);
strv_free(p->unit_path);
p->unit_path = NULL;
#ifdef HAVE_SYSV_COMPAT
strv_free(p->sysvinit_path);
strv_free(p->sysvrcnd_path);
p->sysvinit_path = p->sysvrcnd_path = NULL;
#endif
}
+40
View File
@@ -0,0 +1,40 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#ifndef foopathlookuphfoo
#define foopathlookuphfoo
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
typedef struct LookupPaths {
char **unit_path;
#ifdef HAVE_SYSV_COMPAT
char **sysvinit_path;
char **sysvrcnd_path;
#endif
} LookupPaths;
#include "manager.h"
int user_config_home(char **config_home);
int lookup_paths_init(LookupPaths *p, ManagerRunningAs running_as, bool personal);
void lookup_paths_free(LookupPaths *p);
#endif
+67
View File
@@ -0,0 +1,67 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2011 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/prctl.h>
#include <signal.h>
#include <fcntl.h>
#include "log.h"
#include "util.h"
#include "spawn-ask-password-agent.h"
static pid_t agent_pid = 0;
int ask_password_agent_open(void) {
int r;
if (agent_pid > 0)
return 0;
/* We check STDIN here, not STDOUT, since this is about input,
* not output */
if (!isatty(STDIN_FILENO))
return 0;
r = fork_agent(&agent_pid,
NULL, 0,
SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH,
SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, "--watch", NULL);
if (r < 0)
log_error("Failed to fork TTY ask password agent: %s", strerror(-r));
return r;
}
void ask_password_agent_close(void) {
if (agent_pid <= 0)
return;
/* Inform agent that we are done */
kill(agent_pid, SIGTERM);
kill(agent_pid, SIGCONT);
wait_for_terminate(agent_pid, NULL);
agent_pid = 0;
}
+28
View File
@@ -0,0 +1,28 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#ifndef foospawnaskpasswordagenthfoo
#define foospawnaskpasswordagenthfoo
/***
This file is part of systemd.
Copyright 2011 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
int ask_password_agent_open(void);
void ask_password_agent_close(void);
#endif
+86
View File
@@ -0,0 +1,86 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2011 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/prctl.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/poll.h>
#include "log.h"
#include "util.h"
#include "spawn-polkit-agent.h"
static pid_t agent_pid = 0;
int polkit_agent_open(void) {
int r;
int pipe_fd[2];
char notify_fd[10 + 1];
if (agent_pid > 0)
return 0;
/* We check STDIN here, not STDOUT, since this is about input,
* not output */
if (!isatty(STDIN_FILENO))
return 0;
if (pipe2(pipe_fd, 0) < 0)
return -errno;
snprintf(notify_fd, sizeof(notify_fd), "%i", pipe_fd[1]);
char_array_0(notify_fd);
r = fork_agent(&agent_pid,
&pipe_fd[1], 1,
POLKIT_AGENT_BINARY_PATH,
POLKIT_AGENT_BINARY_PATH, "--notify-fd", notify_fd, NULL);
/* Close the writing side, because that's the one for the agent */
close_nointr_nofail(pipe_fd[1]);
if (r < 0)
log_error("Failed to fork TTY ask password agent: %s", strerror(-r));
else
/* Wait until the agent closes the fd */
fd_wait_for_event(pipe_fd[0], POLLHUP, (usec_t) -1);
close_nointr_nofail(pipe_fd[0]);
return r;
}
void polkit_agent_close(void) {
if (agent_pid <= 0)
return;
/* Inform agent that we are done */
kill(agent_pid, SIGTERM);
kill(agent_pid, SIGCONT);
wait_for_terminate(agent_pid, NULL);
agent_pid = 0;
}
+28
View File
@@ -0,0 +1,28 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#ifndef foospawnpolkitagenthfoo
#define foospawnpolkitagenthfoo
/***
This file is part of systemd.
Copyright 2012 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
int polkit_agent_open(void);
void polkit_agent_close(void);
#endif
+108
View File
@@ -0,0 +1,108 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <string.h>
#include "macro.h"
#include "util.h"
#include "specifier.h"
/*
* Generic infrastructure for replacing %x style specifiers in
* strings. Will call a callback for each replacement.
*
*/
char *specifier_printf(const char *text, const Specifier table[], void *userdata) {
char *r, *t;
const char *f;
bool percent = false;
size_t l;
assert(text);
assert(table);
l = strlen(text);
if (!(r = new(char, l+1)))
return NULL;
t = r;
for (f = text; *f; f++, l--) {
if (percent) {
if (*f == '%')
*(t++) = '%';
else {
const Specifier *i;
for (i = table; i->specifier; i++)
if (i->specifier == *f)
break;
if (i->lookup) {
char *n, *w;
size_t k, j;
if (!(w = i->lookup(i->specifier, i->data, userdata))) {
free(r);
return NULL;
}
j = t - r;
k = strlen(w);
if (!(n = new(char, j + k + l + 1))) {
free(r);
free(w);
return NULL;
}
memcpy(n, r, j);
memcpy(n + j, w, k);
free(r);
free(w);
r = n;
t = n + j + k;
} else {
*(t++) = '%';
*(t++) = *f;
}
}
percent = false;
} else if (*f == '%')
percent = true;
else
*(t++) = *f;
}
*t = 0;
return r;
}
/* Generic handler for simple string replacements */
char* specifier_string(char specifier, void *data, void *userdata) {
return strdup(strempty(data));
}
+37
View File
@@ -0,0 +1,37 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#ifndef foospecifierhfoo
#define foospecifierhfoo
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
typedef char* (*SpecifierCallback)(char specifier, void *data, void *userdata);
typedef struct Specifier {
const char specifier;
const SpecifierCallback lookup;
void *data;
} Specifier;
char *specifier_printf(const char *text, const Specifier table[], void *userdata);
char* specifier_string(char specifier, void *data, void *userdata);
#endif
+644
View File
@@ -0,0 +1,644 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 ProFUSION embedded systems
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/swap.h>
#include <unistd.h>
#include <linux/loop.h>
#include <linux/dm-ioctl.h>
#include <libudev.h>
#include "list.h"
#include "mount-setup.h"
#include "umount.h"
#include "util.h"
typedef struct MountPoint {
char *path;
dev_t devnum;
bool skip_ro;
LIST_FIELDS (struct MountPoint, mount_point);
} MountPoint;
static void mount_point_free(MountPoint **head, MountPoint *m) {
assert(head);
assert(m);
LIST_REMOVE(MountPoint, mount_point, *head, m);
free(m->path);
free(m);
}
static void mount_points_list_free(MountPoint **head) {
assert(head);
while (*head)
mount_point_free(head, *head);
}
static int mount_points_list_get(MountPoint **head) {
FILE *proc_self_mountinfo;
char *path, *p;
unsigned int i;
int r;
assert(head);
if (!(proc_self_mountinfo = fopen("/proc/self/mountinfo", "re")))
return -errno;
for (i = 1;; i++) {
int k;
MountPoint *m;
char *root;
bool skip_ro;
path = p = NULL;
if ((k = fscanf(proc_self_mountinfo,
"%*s " /* (1) mount id */
"%*s " /* (2) parent id */
"%*s " /* (3) major:minor */
"%ms " /* (4) root */
"%ms " /* (5) mount point */
"%*s" /* (6) mount options */
"%*[^-]" /* (7) optional fields */
"- " /* (8) separator */
"%*s " /* (9) file system type */
"%*s" /* (10) mount source */
"%*s" /* (11) mount options 2 */
"%*[^\n]", /* some rubbish at the end */
&root,
&path)) != 2) {
if (k == EOF)
break;
log_warning("Failed to parse /proc/self/mountinfo:%u.", i);
free(path);
continue;
}
/* If we encounter a bind mount, don't try to remount
* the source dir too early */
skip_ro = !streq(root, "/");
free(root);
p = cunescape(path);
free(path);
if (!p) {
r = -ENOMEM;
goto finish;
}
if (mount_point_is_api(p) || mount_point_ignore(p)) {
free(p);
continue;
}
if (!(m = new0(MountPoint, 1))) {
free(p);
r = -ENOMEM;
goto finish;
}
m->path = p;
m->skip_ro = skip_ro;
LIST_PREPEND(MountPoint, mount_point, *head, m);
}
r = 0;
finish:
fclose(proc_self_mountinfo);
return r;
}
static int swap_list_get(MountPoint **head) {
FILE *proc_swaps;
unsigned int i;
int r;
assert(head);
if (!(proc_swaps = fopen("/proc/swaps", "re")))
return (errno == ENOENT) ? 0 : -errno;
(void) fscanf(proc_swaps, "%*s %*s %*s %*s %*s\n");
for (i = 2;; i++) {
MountPoint *swap;
char *dev = NULL, *d;
int k;
if ((k = fscanf(proc_swaps,
"%ms " /* device/file */
"%*s " /* type of swap */
"%*s " /* swap size */
"%*s " /* used */
"%*s\n", /* priority */
&dev)) != 1) {
if (k == EOF)
break;
log_warning("Failed to parse /proc/swaps:%u.", i);
free(dev);
continue;
}
if (endswith(dev, "(deleted)")) {
free(dev);
continue;
}
d = cunescape(dev);
free(dev);
if (!d) {
r = -ENOMEM;
goto finish;
}
if (!(swap = new0(MountPoint, 1))) {
free(d);
r = -ENOMEM;
goto finish;
}
swap->path = d;
LIST_PREPEND(MountPoint, mount_point, *head, swap);
}
r = 0;
finish:
fclose(proc_swaps);
return r;
}
static int loopback_list_get(MountPoint **head) {
int r;
struct udev *udev;
struct udev_enumerate *e = NULL;
struct udev_list_entry *item = NULL, *first = NULL;
assert(head);
if (!(udev = udev_new())) {
r = -ENOMEM;
goto finish;
}
if (!(e = udev_enumerate_new(udev))) {
r = -ENOMEM;
goto finish;
}
if (udev_enumerate_add_match_subsystem(e, "block") < 0 ||
udev_enumerate_add_match_sysname(e, "loop*") < 0) {
r = -EIO;
goto finish;
}
if (udev_enumerate_scan_devices(e) < 0) {
r = -EIO;
goto finish;
}
first = udev_enumerate_get_list_entry(e);
udev_list_entry_foreach(item, first) {
MountPoint *lb;
struct udev_device *d;
char *loop;
const char *dn;
if (!(d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)))) {
r = -ENOMEM;
goto finish;
}
if (!(dn = udev_device_get_devnode(d))) {
udev_device_unref(d);
continue;
}
loop = strdup(dn);
udev_device_unref(d);
if (!loop) {
r = -ENOMEM;
goto finish;
}
if (!(lb = new0(MountPoint, 1))) {
free(loop);
r = -ENOMEM;
goto finish;
}
lb->path = loop;
LIST_PREPEND(MountPoint, mount_point, *head, lb);
}
r = 0;
finish:
if (e)
udev_enumerate_unref(e);
if (udev)
udev_unref(udev);
return r;
}
static int dm_list_get(MountPoint **head) {
int r;
struct udev *udev;
struct udev_enumerate *e = NULL;
struct udev_list_entry *item = NULL, *first = NULL;
assert(head);
if (!(udev = udev_new())) {
r = -ENOMEM;
goto finish;
}
if (!(e = udev_enumerate_new(udev))) {
r = -ENOMEM;
goto finish;
}
if (udev_enumerate_add_match_subsystem(e, "block") < 0 ||
udev_enumerate_add_match_sysname(e, "dm-*") < 0) {
r = -EIO;
goto finish;
}
if (udev_enumerate_scan_devices(e) < 0) {
r = -EIO;
goto finish;
}
first = udev_enumerate_get_list_entry(e);
udev_list_entry_foreach(item, first) {
MountPoint *m;
struct udev_device *d;
dev_t devnum;
char *node;
const char *dn;
if (!(d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)))) {
r = -ENOMEM;
goto finish;
}
devnum = udev_device_get_devnum(d);
dn = udev_device_get_devnode(d);
if (major(devnum) == 0 || !dn) {
udev_device_unref(d);
continue;
}
node = strdup(dn);
udev_device_unref(d);
if (!node) {
r = -ENOMEM;
goto finish;
}
if (!(m = new(MountPoint, 1))) {
free(node);
r = -ENOMEM;
goto finish;
}
m->path = node;
m->devnum = devnum;
LIST_PREPEND(MountPoint, mount_point, *head, m);
}
r = 0;
finish:
if (e)
udev_enumerate_unref(e);
if (udev)
udev_unref(udev);
return r;
}
static int delete_loopback(const char *device) {
int fd, r;
if ((fd = open(device, O_RDONLY|O_CLOEXEC)) < 0)
return errno == ENOENT ? 0 : -errno;
r = ioctl(fd, LOOP_CLR_FD, 0);
close_nointr_nofail(fd);
if (r >= 0)
return 1;
/* ENXIO: not bound, so no error */
if (errno == ENXIO)
return 0;
return -errno;
}
static int delete_dm(dev_t devnum) {
int fd, r;
struct dm_ioctl dm;
assert(major(devnum) != 0);
if ((fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC)) < 0)
return -errno;
zero(dm);
dm.version[0] = DM_VERSION_MAJOR;
dm.version[1] = DM_VERSION_MINOR;
dm.version[2] = DM_VERSION_PATCHLEVEL;
dm.data_size = sizeof(dm);
dm.dev = devnum;
r = ioctl(fd, DM_DEV_REMOVE, &dm);
close_nointr_nofail(fd);
return r >= 0 ? 0 : -errno;
}
static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_error) {
MountPoint *m, *n;
int n_failed = 0;
assert(head);
LIST_FOREACH_SAFE(mount_point, m, n, *head) {
if (path_equal(m->path, "/")
#ifndef HAVE_SPLIT_USR
|| path_equal(m->path, "/usr")
#endif
) {
n_failed++;
continue;
}
/* Trying to umount. Forcing to umount if busy (only for NFS mounts) */
if (umount2(m->path, MNT_FORCE) == 0) {
log_info("Unmounted %s.", m->path);
if (changed)
*changed = true;
mount_point_free(head, m);
} else if (log_error) {
log_warning("Could not unmount %s: %m", m->path);
n_failed++;
}
}
return n_failed;
}
static int mount_points_list_remount_read_only(MountPoint **head, bool *changed) {
MountPoint *m, *n;
int n_failed = 0;
assert(head);
LIST_FOREACH_SAFE(mount_point, m, n, *head) {
if (m->skip_ro) {
n_failed++;
continue;
}
/* Trying to remount read-only */
if (mount(NULL, m->path, NULL, MS_MGC_VAL|MS_REMOUNT|MS_RDONLY, NULL) == 0) {
if (changed)
*changed = true;
mount_point_free(head, m);
} else {
log_warning("Could not remount as read-only %s: %m", m->path);
n_failed++;
}
}
return n_failed;
}
static int swap_points_list_off(MountPoint **head, bool *changed) {
MountPoint *m, *n;
int n_failed = 0;
assert(head);
LIST_FOREACH_SAFE(mount_point, m, n, *head) {
if (swapoff(m->path) == 0) {
if (changed)
*changed = true;
mount_point_free(head, m);
} else {
log_warning("Could not deactivate swap %s: %m", m->path);
n_failed++;
}
}
return n_failed;
}
static int loopback_points_list_detach(MountPoint **head, bool *changed) {
MountPoint *m, *n;
int n_failed = 0, k;
struct stat root_st;
assert(head);
k = lstat("/", &root_st);
LIST_FOREACH_SAFE(mount_point, m, n, *head) {
int r;
struct stat loopback_st;
if (k >= 0 &&
major(root_st.st_dev) != 0 &&
lstat(m->path, &loopback_st) >= 0 &&
root_st.st_dev == loopback_st.st_rdev) {
n_failed ++;
continue;
}
if ((r = delete_loopback(m->path)) >= 0) {
if (r > 0 && changed)
*changed = true;
mount_point_free(head, m);
} else {
log_warning("Could not delete loopback %s: %m", m->path);
n_failed++;
}
}
return n_failed;
}
static int dm_points_list_detach(MountPoint **head, bool *changed) {
MountPoint *m, *n;
int n_failed = 0, k;
struct stat root_st;
assert(head);
k = lstat("/", &root_st);
LIST_FOREACH_SAFE(mount_point, m, n, *head) {
int r;
if (k >= 0 &&
major(root_st.st_dev) != 0 &&
root_st.st_dev == m->devnum) {
n_failed ++;
continue;
}
if ((r = delete_dm(m->devnum)) >= 0) {
if (r > 0 && changed)
*changed = true;
mount_point_free(head, m);
} else {
log_warning("Could not delete dm %s: %m", m->path);
n_failed++;
}
}
return n_failed;
}
int umount_all(bool *changed) {
int r;
bool umount_changed;
LIST_HEAD(MountPoint, mp_list_head);
LIST_HEAD_INIT(MountPoint, mp_list_head);
r = mount_points_list_get(&mp_list_head);
if (r < 0)
goto end;
/* retry umount, until nothing can be umounted anymore */
do {
umount_changed = false;
mount_points_list_umount(&mp_list_head, &umount_changed, false);
if (umount_changed)
*changed = true;
} while (umount_changed);
/* umount one more time with logging enabled */
r = mount_points_list_umount(&mp_list_head, &umount_changed, true);
if (r <= 0)
goto end;
r = mount_points_list_remount_read_only(&mp_list_head, changed);
end:
mount_points_list_free(&mp_list_head);
return r;
}
int swapoff_all(bool *changed) {
int r;
LIST_HEAD(MountPoint, swap_list_head);
LIST_HEAD_INIT(MountPoint, swap_list_head);
r = swap_list_get(&swap_list_head);
if (r < 0)
goto end;
r = swap_points_list_off(&swap_list_head, changed);
end:
mount_points_list_free(&swap_list_head);
return r;
}
int loopback_detach_all(bool *changed) {
int r;
LIST_HEAD(MountPoint, loopback_list_head);
LIST_HEAD_INIT(MountPoint, loopback_list_head);
r = loopback_list_get(&loopback_list_head);
if (r < 0)
goto end;
r = loopback_points_list_detach(&loopback_list_head, changed);
end:
mount_points_list_free(&loopback_list_head);
return r;
}
int dm_detach_all(bool *changed) {
int r;
LIST_HEAD(MountPoint, dm_list_head);
LIST_HEAD_INIT(MountPoint, dm_list_head);
r = dm_list_get(&dm_list_head);
if (r < 0)
goto end;
r = dm_points_list_detach(&dm_list_head, changed);
end:
mount_points_list_free(&dm_list_head);
return r;
}
+33
View File
@@ -0,0 +1,33 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#ifndef fooumounthfoo
#define fooumounthfoo
/***
This file is part of systemd.
Copyright 2010 ProFUSION embedded systems
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
int umount_all(bool *changed);
int swapoff_all(bool *changed);
int loopback_detach_all(bool *changed);
int dm_detach_all(bool *changed);
#endif
+448
View File
@@ -0,0 +1,448 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <errno.h>
#include <string.h>
#include <assert.h>
#include "util.h"
#include "unit-name.h"
#define VALID_CHARS \
"0123456789" \
"abcdefghijklmnopqrstuvwxyz" \
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
":-_.\\"
bool unit_name_is_valid_no_type(const char *n, bool template_ok) {
const char *e, *i, *at;
/* Valid formats:
*
* string@instance.suffix
* string.suffix
*/
assert(n);
if (strlen(n) >= UNIT_NAME_MAX)
return false;
e = strrchr(n, '.');
if (!e || e == n)
return false;
for (i = n, at = NULL; i < e; i++) {
if (*i == '@' && !at)
at = i;
if (!strchr("@" VALID_CHARS, *i))
return false;
}
if (at) {
if (at == n)
return false;
if (!template_ok && at+1 == e)
return false;
}
return true;
}
bool unit_instance_is_valid(const char *i) {
assert(i);
/* The max length depends on the length of the string, so we
* don't really check this here. */
if (i[0] == 0)
return false;
/* We allow additional @ in the instance string, we do not
* allow them in the prefix! */
for (; *i; i++)
if (!strchr("@" VALID_CHARS, *i))
return false;
return true;
}
bool unit_prefix_is_valid(const char *p) {
/* We don't allow additional @ in the instance string */
if (p[0] == 0)
return false;
for (; *p; p++)
if (!strchr(VALID_CHARS, *p))
return false;
return true;
}
int unit_name_to_instance(const char *n, char **instance) {
const char *p, *d;
char *i;
assert(n);
assert(instance);
/* Everything past the first @ and before the last . is the instance */
if (!(p = strchr(n, '@'))) {
*instance = NULL;
return 0;
}
assert_se(d = strrchr(n, '.'));
assert(p < d);
if (!(i = strndup(p+1, d-p-1)))
return -ENOMEM;
*instance = i;
return 0;
}
char *unit_name_to_prefix_and_instance(const char *n) {
const char *d;
assert(n);
assert_se(d = strrchr(n, '.'));
return strndup(n, d - n);
}
char *unit_name_to_prefix(const char *n) {
const char *p;
if ((p = strchr(n, '@')))
return strndup(n, p - n);
return unit_name_to_prefix_and_instance(n);
}
char *unit_name_change_suffix(const char *n, const char *suffix) {
char *e, *r;
size_t a, b;
assert(n);
assert(unit_name_is_valid_no_type(n, true));
assert(suffix);
assert_se(e = strrchr(n, '.'));
a = e - n;
b = strlen(suffix);
if (!(r = new(char, a + b + 1)))
return NULL;
memcpy(r, n, a);
memcpy(r+a, suffix, b+1);
return r;
}
char *unit_name_build(const char *prefix, const char *instance, const char *suffix) {
assert(prefix);
assert(unit_prefix_is_valid(prefix));
assert(!instance || unit_instance_is_valid(instance));
assert(suffix);
if (!instance)
return strappend(prefix, suffix);
return join(prefix, "@", instance, suffix, NULL);
}
static char* do_escape(const char *f, char *t) {
assert(f);
assert(t);
for (; *f; f++) {
if (*f == '/')
*(t++) = '-';
else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f)) {
*(t++) = '\\';
*(t++) = 'x';
*(t++) = hexchar(*f >> 4);
*(t++) = hexchar(*f);
} else
*(t++) = *f;
}
return t;
}
char *unit_name_build_escape(const char *prefix, const char *instance, const char *suffix) {
char *r, *t;
size_t a, b, c;
assert(prefix);
assert(suffix);
/* Takes a arbitrary string for prefix and instance plus a
* suffix and makes a nice string suitable as unit name of it,
* escaping all weird chars on the way.
*
* / becomes ., and all chars not allowed in a unit name get
* escaped as \xFF, including \ and ., of course. This
* escaping is hence reversible.
*
* This is primarily useful to make nice unit names from
* strings, but is actually useful for any kind of string.
*/
a = strlen(prefix);
c = strlen(suffix);
if (instance) {
b = strlen(instance);
if (!(r = new(char, a*4 + 1 + b*4 + c + 1)))
return NULL;
t = do_escape(prefix, r);
*(t++) = '@';
t = do_escape(instance, t);
} else {
if (!(r = new(char, a*4 + c + 1)))
return NULL;
t = do_escape(prefix, r);
}
strcpy(t, suffix);
return r;
}
char *unit_name_escape(const char *f) {
char *r, *t;
if (!(r = new(char, strlen(f)*4+1)))
return NULL;
t = do_escape(f, r);
*t = 0;
return r;
}
char *unit_name_unescape(const char *f) {
char *r, *t;
assert(f);
if (!(r = strdup(f)))
return NULL;
for (t = r; *f; f++) {
if (*f == '-')
*(t++) = '/';
else if (*f == '\\') {
int a, b;
if (f[1] != 'x' ||
(a = unhexchar(f[2])) < 0 ||
(b = unhexchar(f[3])) < 0) {
/* Invalid escape code, let's take it literal then */
*(t++) = '\\';
} else {
*(t++) = (char) ((a << 4) | b);
f += 3;
}
} else
*(t++) = *f;
}
*t = 0;
return r;
}
bool unit_name_is_template(const char *n) {
const char *p;
assert(n);
if (!(p = strchr(n, '@')))
return false;
return p[1] == '.';
}
char *unit_name_replace_instance(const char *f, const char *i) {
const char *p, *e;
char *r, *k;
size_t a;
assert(f);
p = strchr(f, '@');
assert_se(e = strrchr(f, '.'));
a = p - f;
if (p) {
size_t b;
b = strlen(i);
if (!(r = new(char, a + 1 + b + strlen(e) + 1)))
return NULL;
k = mempcpy(r, f, a + 1);
k = mempcpy(k, i, b);
} else {
if (!(r = new(char, a + strlen(e) + 1)))
return NULL;
k = mempcpy(r, f, a);
}
strcpy(k, e);
return r;
}
char *unit_name_template(const char *f) {
const char *p, *e;
char *r;
size_t a;
if (!(p = strchr(f, '@')))
return strdup(f);
assert_se(e = strrchr(f, '.'));
a = p - f + 1;
if (!(r = new(char, a + strlen(e) + 1)))
return NULL;
strcpy(mempcpy(r, f, a), e);
return r;
}
char *unit_name_from_path(const char *path, const char *suffix) {
char *p, *r;
assert(path);
assert(suffix);
if (!(p = strdup(path)))
return NULL;
path_kill_slashes(p);
path = p[0] == '/' ? p + 1 : p;
if (path[0] == 0) {
free(p);
return strappend("-", suffix);
}
r = unit_name_build_escape(path, NULL, suffix);
free(p);
return r;
}
char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) {
char *p, *r;
assert(path);
assert(suffix);
if (!(p = strdup(path)))
return NULL;
path_kill_slashes(p);
path = p[0] == '/' ? p + 1 : p;
if (path[0] == 0) {
free(p);
return unit_name_build_escape(prefix, "-", suffix);
}
r = unit_name_build_escape(prefix, path, suffix);
free(p);
return r;
}
char *unit_name_to_path(const char *name) {
char *w, *e;
assert(name);
if (!(w = unit_name_to_prefix(name)))
return NULL;
e = unit_name_unescape(w);
free(w);
if (!e)
return NULL;
if (e[0] != '/') {
w = strappend("/", e);
free(e);
if (!w)
return NULL;
e = w;
}
return e;
}
char *unit_name_path_unescape(const char *f) {
char *e;
assert(f);
if (!(e = unit_name_unescape(f)))
return NULL;
if (e[0] != '/') {
char *w;
w = strappend("/", e);
free(e);
if (!w)
return NULL;
e = w;
}
return e;
}
+57
View File
@@ -0,0 +1,57 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#ifndef foounitnamehfoo
#define foounitnamehfoo
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdbool.h>
#define UNIT_NAME_MAX 256
int unit_name_to_instance(const char *n, char **instance);
char* unit_name_to_prefix(const char *n);
char* unit_name_to_prefix_and_instance(const char *n);
bool unit_name_is_valid_no_type(const char *n, bool template_ok);
bool unit_prefix_is_valid(const char *p);
bool unit_instance_is_valid(const char *i);
char *unit_name_change_suffix(const char *n, const char *suffix);
char *unit_name_build(const char *prefix, const char *instance, const char *suffix);
char *unit_name_build_escape(const char *prefix, const char *instance, const char *suffix);
char *unit_name_escape(const char *f);
char *unit_name_unescape(const char *f);
char *unit_name_path_unescape(const char *f);
bool unit_name_is_template(const char *n);
char *unit_name_replace_instance(const char *f, const char *i);
char *unit_name_template(const char *f);
char *unit_name_from_path(const char *path, const char *suffix);
char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix);
char *unit_name_to_path(const char *name);
#endif
+430
View File
@@ -0,0 +1,430 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <utmpx.h>
#include <errno.h>
#include <assert.h>
#include <string.h>
#include <sys/utsname.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/poll.h>
#include "macro.h"
#include "utmp-wtmp.h"
int utmp_get_runlevel(int *runlevel, int *previous) {
struct utmpx lookup, *found;
int r;
const char *e;
assert(runlevel);
/* If these values are set in the environment this takes
* precedence. Presumably, sysvinit does this to work around a
* race condition that would otherwise exist where we'd always
* go to disk and hence might read runlevel data that might be
* very new and does not apply to the current script being
* executed. */
if ((e = getenv("RUNLEVEL")) && e[0] > 0) {
*runlevel = e[0];
if (previous) {
/* $PREVLEVEL seems to be an Upstart thing */
if ((e = getenv("PREVLEVEL")) && e[0] > 0)
*previous = e[0];
else
*previous = 0;
}
return 0;
}
if (utmpxname(_PATH_UTMPX) < 0)
return -errno;
setutxent();
zero(lookup);
lookup.ut_type = RUN_LVL;
if (!(found = getutxid(&lookup)))
r = -errno;
else {
int a, b;
a = found->ut_pid & 0xFF;
b = (found->ut_pid >> 8) & 0xFF;
if (a < 0 || b < 0)
r = -EIO;
else {
*runlevel = a;
if (previous)
*previous = b;
r = 0;
}
}
endutxent();
return r;
}
static void init_timestamp(struct utmpx *store, usec_t t) {
assert(store);
zero(*store);
if (t <= 0)
t = now(CLOCK_REALTIME);
store->ut_tv.tv_sec = t / USEC_PER_SEC;
store->ut_tv.tv_usec = t % USEC_PER_SEC;
}
static void init_entry(struct utmpx *store, usec_t t) {
struct utsname uts;
assert(store);
init_timestamp(store, t);
zero(uts);
if (uname(&uts) >= 0)
strncpy(store->ut_host, uts.release, sizeof(store->ut_host));
strncpy(store->ut_line, "~", sizeof(store->ut_line)); /* or ~~ ? */
strncpy(store->ut_id, "~~", sizeof(store->ut_id));
}
static int write_entry_utmp(const struct utmpx *store) {
int r;
assert(store);
/* utmp is similar to wtmp, but there is only one entry for
* each entry type resp. user; i.e. basically a key/value
* table. */
if (utmpxname(_PATH_UTMPX) < 0)
return -errno;
setutxent();
if (!pututxline(store))
r = -errno;
else
r = 0;
endutxent();
return r;
}
static int write_entry_wtmp(const struct utmpx *store) {
assert(store);
/* wtmp is a simple append-only file where each entry is
simply appended to * the end; i.e. basically a log. */
errno = 0;
updwtmpx(_PATH_WTMPX, store);
return -errno;
}
static int write_utmp_wtmp(const struct utmpx *store_utmp, const struct utmpx *store_wtmp) {
int r, s;
r = write_entry_utmp(store_utmp);
s = write_entry_wtmp(store_wtmp);
if (r >= 0)
r = s;
/* If utmp/wtmp have been disabled, that's a good thing, hence
* ignore the errors */
if (r == -ENOENT)
r = 0;
return r;
}
static int write_entry_both(const struct utmpx *store) {
return write_utmp_wtmp(store, store);
}
int utmp_put_shutdown(void) {
struct utmpx store;
init_entry(&store, 0);
store.ut_type = RUN_LVL;
strncpy(store.ut_user, "shutdown", sizeof(store.ut_user));
return write_entry_both(&store);
}
int utmp_put_reboot(usec_t t) {
struct utmpx store;
init_entry(&store, t);
store.ut_type = BOOT_TIME;
strncpy(store.ut_user, "reboot", sizeof(store.ut_user));
return write_entry_both(&store);
}
static const char *sanitize_id(const char *id) {
size_t l;
assert(id);
l = strlen(id);
if (l <= sizeof(((struct utmpx*) NULL)->ut_id))
return id;
return id + l - sizeof(((struct utmpx*) NULL)->ut_id);
}
int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line) {
struct utmpx store;
assert(id);
init_timestamp(&store, 0);
store.ut_type = INIT_PROCESS;
store.ut_pid = pid;
store.ut_session = sid;
strncpy(store.ut_id, sanitize_id(id), sizeof(store.ut_id));
if (line)
strncpy(store.ut_line, file_name_from_path(line), sizeof(store.ut_line));
return write_entry_both(&store);
}
int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) {
struct utmpx lookup, store, store_wtmp, *found;
assert(id);
setutxent();
zero(lookup);
lookup.ut_type = INIT_PROCESS; /* looks for DEAD_PROCESS, LOGIN_PROCESS, USER_PROCESS, too */
strncpy(lookup.ut_id, sanitize_id(id), sizeof(lookup.ut_id));
if (!(found = getutxid(&lookup)))
return 0;
if (found->ut_pid != pid)
return 0;
memcpy(&store, found, sizeof(store));
store.ut_type = DEAD_PROCESS;
store.ut_exit.e_termination = code;
store.ut_exit.e_exit = status;
zero(store.ut_user);
zero(store.ut_host);
zero(store.ut_tv);
memcpy(&store_wtmp, &store, sizeof(store_wtmp));
/* wtmp wants the current time */
init_timestamp(&store_wtmp, 0);
return write_utmp_wtmp(&store, &store_wtmp);
}
int utmp_put_runlevel(int runlevel, int previous) {
struct utmpx store;
int r;
assert(runlevel > 0);
if (previous <= 0) {
/* Find the old runlevel automatically */
if ((r = utmp_get_runlevel(&previous, NULL)) < 0) {
if (r != -ESRCH)
return r;
previous = 0;
}
}
if (previous == runlevel)
return 0;
init_entry(&store, 0);
store.ut_type = RUN_LVL;
store.ut_pid = (runlevel & 0xFF) | ((previous & 0xFF) << 8);
strncpy(store.ut_user, "runlevel", sizeof(store.ut_user));
return write_entry_both(&store);
}
#define TIMEOUT_MSEC 50
static int write_to_terminal(const char *tty, const char *message) {
int fd, r;
const char *p;
size_t left;
usec_t end;
assert(tty);
assert(message);
if ((fd = open(tty, O_WRONLY|O_NDELAY|O_NOCTTY|O_CLOEXEC)) < 0)
return -errno;
if (!isatty(fd)) {
r = -errno;
goto finish;
}
p = message;
left = strlen(message);
end = now(CLOCK_MONOTONIC) + TIMEOUT_MSEC*USEC_PER_MSEC;
while (left > 0) {
ssize_t n;
struct pollfd pollfd;
usec_t t;
int k;
t = now(CLOCK_MONOTONIC);
if (t >= end) {
r = -ETIME;
goto finish;
}
zero(pollfd);
pollfd.fd = fd;
pollfd.events = POLLOUT;
if ((k = poll(&pollfd, 1, (end - t) / USEC_PER_MSEC)) < 0)
return -errno;
if (k <= 0) {
r = -ETIME;
goto finish;
}
if ((n = write(fd, p, left)) < 0) {
if (errno == EAGAIN)
continue;
r = -errno;
goto finish;
}
assert((size_t) n <= left);
p += n;
left -= n;
}
r = 0;
finish:
close_nointr_nofail(fd);
return r;
}
int utmp_wall(const char *message, bool (*match_tty)(const char *tty)) {
struct utmpx *u;
char date[FORMAT_TIMESTAMP_MAX];
char *text = NULL, *hn = NULL, *un = NULL, *tty = NULL;
int r;
if (!(hn = gethostname_malloc()) ||
!(un = getlogname_malloc())) {
r = -ENOMEM;
goto finish;
}
getttyname_harder(STDIN_FILENO, &tty);
if (asprintf(&text,
"\a\r\n"
"Broadcast message from %s@%s%s%s (%s):\r\n\r\n"
"%s\r\n\r\n",
un, hn,
tty ? " on " : "", strempty(tty),
format_timestamp(date, sizeof(date), now(CLOCK_REALTIME)),
message) < 0) {
r = -ENOMEM;
goto finish;
}
setutxent();
r = 0;
while ((u = getutxent())) {
int q;
const char *path;
char *buf = NULL;
if (u->ut_type != USER_PROCESS || u->ut_user[0] == 0)
continue;
if (path_startswith(u->ut_line, "/dev/"))
path = u->ut_line;
else {
if (asprintf(&buf, "/dev/%s", u->ut_line) < 0) {
r = -ENOMEM;
goto finish;
}
path = buf;
}
if (!match_tty || match_tty(path))
if ((q = write_to_terminal(path, text)) < 0)
r = q;
free(buf);
}
finish:
free(hn);
free(un);
free(tty);
free(text);
return r;
}
+38
View File
@@ -0,0 +1,38 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#ifndef fooutmpwtmphfoo
#define fooutmpwtmphfoo
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "util.h"
int utmp_get_runlevel(int *runlevel, int *previous);
int utmp_put_shutdown(void);
int utmp_put_reboot(usec_t timestamp);
int utmp_put_runlevel(int runlevel, int previous);
int utmp_put_dead_process(const char *id, pid_t pid, int code, int status);
int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line);
int utmp_wall(const char *message, bool (*match_tty)(const char *tty));
#endif
+169
View File
@@ -0,0 +1,169 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2012 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <sys/ioctl.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/watchdog.h>
#include "watchdog.h"
#include "log.h"
static int watchdog_fd = -1;
static usec_t watchdog_timeout = (usec_t) -1;
static int update_timeout(void) {
int r;
if (watchdog_fd < 0)
return 0;
if (watchdog_timeout == (usec_t) -1)
return 0;
else if (watchdog_timeout == 0) {
int flags;
flags = WDIOS_DISABLECARD;
r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags);
if (r < 0) {
log_warning("Failed to disable hardware watchdog: %m");
return -errno;
}
} else {
int sec, flags;
char buf[FORMAT_TIMESPAN_MAX];
sec = (int) ((watchdog_timeout + USEC_PER_SEC - 1) / USEC_PER_SEC);
r = ioctl(watchdog_fd, WDIOC_SETTIMEOUT, &sec);
if (r < 0) {
log_warning("Failed to set timeout to %is: %m", sec);
return -errno;
}
watchdog_timeout = (usec_t) sec * USEC_PER_SEC;
log_info("Set hardware watchdog to %s.", format_timespan(buf, sizeof(buf), watchdog_timeout));
flags = WDIOS_ENABLECARD;
r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags);
if (r < 0) {
log_warning("Failed to enable hardware watchdog: %m");
return -errno;
}
r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0);
if (r < 0) {
log_warning("Failed to ping hardware watchdog: %m");
return -errno;
}
}
return 0;
}
static int open_watchdog(void) {
struct watchdog_info ident;
if (watchdog_fd >= 0)
return 0;
watchdog_fd = open("/dev/watchdog", O_WRONLY|O_CLOEXEC);
if (watchdog_fd < 0)
return -errno;
if (ioctl(watchdog_fd, WDIOC_GETSUPPORT, &ident) >= 0)
log_info("Hardware watchdog '%s', version %x",
ident.identity,
ident.firmware_version);
return update_timeout();
}
int watchdog_set_timeout(usec_t *usec) {
int r;
watchdog_timeout = *usec;
/* If we didn't open the watchdog yet and didn't get any
* explicit timeout value set, don't do anything */
if (watchdog_fd < 0 && watchdog_timeout == (usec_t) -1)
return 0;
if (watchdog_fd < 0)
r = open_watchdog();
else
r = update_timeout();
*usec = watchdog_timeout;
return r;
}
int watchdog_ping(void) {
int r;
if (watchdog_fd < 0) {
r = open_watchdog();
if (r < 0)
return r;
}
r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0);
if (r < 0) {
log_warning("Failed to ping hardware watchdog: %m");
return -errno;
}
return 0;
}
void watchdog_close(bool disarm) {
int r;
if (watchdog_fd < 0)
return;
if (disarm) {
int flags;
/* Explicitly disarm it */
flags = WDIOS_DISABLECARD;
r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags);
if (r < 0)
log_warning("Failed to disable hardware watchdog: %m");
/* To be sure, use magic close logic, too */
for (;;) {
static const char v = 'V';
if (write(watchdog_fd, &v, 1) > 0)
break;
if (errno != EINTR) {
log_error("Failed to disarm watchdog timer: %m");
break;
}
}
}
close_nointr_nofail(watchdog_fd);
watchdog_fd = -1;
}
+31
View File
@@ -0,0 +1,31 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#ifndef foowatchdoghfoo
#define foowatchdoghfoo
/***
This file is part of systemd.
Copyright 2012 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "util.h"
int watchdog_set_timeout(usec_t *usec);
int watchdog_ping(void);
void watchdog_close(bool disarm);
#endif