timedated.c   timedated.c 
skipping to change at line 25 skipping to change at line 25
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details. Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>. along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/ ***/
#include <errno.h> #include <errno.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <sys/capability.h>
#include "sd-id128.h" #include "sd-id128.h"
#include "sd-messages.h" #include "sd-messages.h"
#include "sd-event.h" #include "sd-event.h"
#include "sd-bus.h" #include "sd-bus.h"
#include "util.h" #include "util.h"
#include "strv.h" #include "strv.h"
#include "def.h" #include "def.h"
#include "clock-util.h" #include "clock-util.h"
skipping to change at line 54 skipping to change at line 55
#define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n" #define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n"
typedef struct Context { typedef struct Context {
char *zone; char *zone;
bool local_rtc; bool local_rtc;
bool can_ntp; bool can_ntp;
bool use_ntp; bool use_ntp;
Hashmap *polkit_registry; Hashmap *polkit_registry;
} Context; } Context;
static void context_free(Context *c, sd_bus *bus) { static void context_free(Context *c) {
assert(c); assert(c);
free(c->zone); free(c->zone);
bus_verify_polkit_async_registry_free(bus, c->polkit_registry); bus_verify_polkit_async_registry_free(c->polkit_registry);
}
static bool valid_timezone(const char *name) {
const char *p;
char *t;
bool slash = false;
int r;
struct stat st;
assert(name);
if (*name == '/' || *name == 0)
return false;
for (p = name; *p; p++) {
if (!(*p >= '0' && *p <= '9') &&
!(*p >= 'a' && *p <= 'z') &&
!(*p >= 'A' && *p <= 'Z') &&
!(*p == '-' || *p == '_' || *p == '+' || *p == '/'))
return false;
if (*p == '/') {
if (slash)
return false;
slash = true;
} else
slash = false;
}
if (slash)
return false;
t = strappend("/usr/share/zoneinfo/", name);
if (!t)
return false;
r = stat(t, &st);
free(t);
if (r < 0)
return false;
if (!S_ISREG(st.st_mode))
return false;
return true;
} }
static int context_read_data(Context *c) { static int context_read_data(Context *c) {
_cleanup_free_ char *t = NULL; _cleanup_free_ char *t = NULL;
int r; int r;
assert(c); assert(c);
r = readlink_malloc("/etc/localtime", &t); r = readlink_malloc("/etc/localtime", &t);
if (r < 0) { if (r < 0) {
skipping to change at line 230 skipping to change at line 183
return -errno; return -errno;
return 0; return 0;
} }
} }
label_init("/etc"); label_init("/etc");
return write_string_file_atomic_label("/etc/adjtime", w); return write_string_file_atomic_label("/etc/adjtime", w);
} }
static char** get_ntp_services(void) {
_cleanup_strv_free_ char **r = NULL, **files = NULL;
char **i;
int k;
k = conf_files_list(&files, ".list", NULL,
"/etc/systemd/ntp-units.d",
"/run/systemd/ntp-units.d",
"/usr/local/lib/systemd/ntp-units.d",
"/usr/lib/systemd/ntp-units.d",
NULL);
if (k < 0)
return NULL;
STRV_FOREACH(i, files) {
_cleanup_fclose_ FILE *f;
f = fopen(*i, "re");
if (!f)
continue;
for (;;) {
char line[PATH_MAX], *l;
if (!fgets(line, sizeof(line), f)) {
if (ferror(f))
log_error("Failed to read NTP unit
file: %m");
break;
}
l = strstrip(line);
if (l[0] == 0 || l[0] == '#')
continue;
if (strv_extend(&r, l) < 0) {
log_oom();
return NULL;
}
}
}
i = r;
r = NULL; /* avoid cleanup */
return strv_uniq(i);
}
static int context_read_ntp(Context *c, sd_bus *bus) { static int context_read_ntp(Context *c, sd_bus *bus) {
_cleanup_strv_free_ char **l; _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
char **i; sd_bus_message *reply = NULL;
const char *s;
int r; int r;
assert(c); assert(c);
assert(bus); assert(bus);
l = get_ntp_services(); r = sd_bus_call_method(
STRV_FOREACH(i, l) { bus,
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_ "org.freedesktop.systemd1",
NULL; "/org/freedesktop/systemd1",
sd_bus_message *reply = NULL; "org.freedesktop.systemd1.Manager",
const char *s; "GetUnitFileState",
&error,
r = sd_bus_call_method( &reply,
bus, "s",
"org.freedesktop.systemd1", "systemd-timesyncd.service");
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"GetUnitFileState",
&error,
&reply,
"s",
*i);
if (r < 0) {
/* This implementation does not exist. Try the next
one. */
if (sd_bus_error_has_name(&error, SD_BUS_ERROR_FILE
_NOT_FOUND))
continue;
return r; if (r < 0) {
} if (sd_bus_error_has_name(&error, SD_BUS_ERROR_FILE_NOT_FOU
ND) ||
sd_bus_error_has_name(&error, "org.freedesktop.systemd1
.LoadFailed") ||
sd_bus_error_has_name(&error, "org.freedesktop.systemd1
.NoSuchUnit"))
return 0;
r = sd_bus_message_read(reply, "s", &s); return r;
if (r < 0) }
return r;
c->can_ntp = true; r = sd_bus_message_read(reply, "s", &s);
c->use_ntp = STR_IN_SET(s, "enabled", "enabled-runtime"); if (r < 0)
return r;
return 0; c->can_ntp = true;
} c->use_ntp = STR_IN_SET(s, "enabled", "enabled-runtime");
return 0; return 0;
} }
static int context_start_ntp(Context *c, sd_bus *bus, sd_bus_error *error) { static int context_start_ntp(Context *c, sd_bus *bus, sd_bus_error *error) {
_cleanup_strv_free_ char **l = NULL;
char **i;
int r; int r;
assert(c); assert(c);
assert(bus); assert(bus);
assert(error); assert(error);
l = get_ntp_services(); if (c->use_ntp)
STRV_FOREACH(i, l) { r = sd_bus_call_method(
bus,
if (c->use_ntp) "org.freedesktop.systemd1",
r = sd_bus_call_method( "/org/freedesktop/systemd1",
bus, "org.freedesktop.systemd1.Manager",
"org.freedesktop.systemd1", "StartUnit",
"/org/freedesktop/systemd1", error,
"org.freedesktop.systemd1.Manager", NULL,
"StartUnit", "ss",
error, "systemd-timesyncd.service",
NULL, "replace");
"ss", *i, "replace"); else
else r = sd_bus_call_method(
r = sd_bus_call_method( bus,
bus, "org.freedesktop.systemd1",
"org.freedesktop.systemd1", "/org/freedesktop/systemd1",
"/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager",
"org.freedesktop.systemd1.Manager", "StopUnit",
"StopUnit", error,
error, NULL,
NULL, "ss",
"ss", *i, "replace"); "systemd-timesyncd.service",
"replace");
if (r < 0) {
if (sd_bus_error_has_name(error, SD_BUS_ERROR_FILE_
NOT_FOUND) ||
sd_bus_error_has_name(error, "org.freedesktop.s
ystemd1.LoadFailed") ||
sd_bus_error_has_name(error, "org.freedesktop.s
ystemd1.NoSuchUnit")) {
/* This implementation does not exist. Try
the next one. */
sd_bus_error_free(error);
continue;
}
return r; if (r < 0) {
if (sd_bus_error_has_name(error, SD_BUS_ERROR_FILE_NOT_FOUN
D) ||
sd_bus_error_has_name(error, "org.freedesktop.systemd1.
LoadFailed") ||
sd_bus_error_has_name(error, "org.freedesktop.systemd1.
NoSuchUnit")) {
sd_bus_error_set_const(error, "org.freedesktop.time
date1.NoNTPSupport", "NTP not supported.");
return -ENOTSUP;
} }
return 1; return r;
} }
sd_bus_error_set_const(error, "org.freedesktop.timedate1.NoNTPSuppo return 0;
rt", "NTP not supported.");
return -ENOTSUP;
} }
static int context_enable_ntp(Context*c, sd_bus *bus, sd_bus_error *error) { static int context_enable_ntp(Context*c, sd_bus *bus, sd_bus_error *error) {
_cleanup_strv_free_ char **l = NULL;
char **i;
int r; int r;
assert(c); assert(c);
assert(bus); assert(bus);
assert(error); assert(error);
l = get_ntp_services(); if (c->use_ntp)
STRV_FOREACH(i, l) {
if (c->use_ntp)
r = sd_bus_call_method(
bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"EnableUnitFiles",
error,
NULL,
"asbb", 1, *i, false, true);
else
r = sd_bus_call_method(
bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"DisableUnitFiles",
error,
NULL,
"asb", 1, *i, false);
if (r < 0) {
if (sd_bus_error_has_name(error, SD_BUS_ERROR_FILE_
NOT_FOUND)) {
/* This implementation does not exist. Try
the next one. */
sd_bus_error_free(error);
continue;
}
return r;
}
r = sd_bus_call_method( r = sd_bus_call_method(
bus, bus,
"org.freedesktop.systemd1", "org.freedesktop.systemd1",
"/org/freedesktop/systemd1", "/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager", "org.freedesktop.systemd1.Manager",
"Reload", "EnableUnitFiles",
error, error,
NULL, NULL,
NULL); "asbb", 1,
if (r < 0) "systemd-timesyncd.service",
return r; false, true);
else
r = sd_bus_call_method(
bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"DisableUnitFiles",
error,
NULL,
"asb", 1,
"systemd-timesyncd.service",
false);
return 1; if (r < 0) {
if (sd_bus_error_has_name(error, SD_BUS_ERROR_FILE_NOT_FOUN
D)) {
sd_bus_error_set_const(error, "org.freedesktop.time
date1.NoNTPSupport", "NTP not supported.");
return -ENOTSUP;
}
return r;
} }
sd_bus_error_set_const(error, "org.freedesktop.timedate1.NoNTPSuppo r = sd_bus_call_method(
rt", "NTP not supported."); bus,
return -ENOTSUP; "org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"Reload",
error,
NULL,
NULL);
if (r < 0)
return r;
return 0;
} }
static int property_get_rtc_time( static int property_get_rtc_time(
sd_bus *bus, sd_bus *bus,
const char *path, const char *path,
const char *interface, const char *interface,
const char *property, const char *property,
sd_bus_message *reply, sd_bus_message *reply,
void *userdata, void *userdata,
sd_bus_error *error) { sd_bus_error *error) {
skipping to change at line 505 skipping to change at line 392
int r; int r;
assert(bus); assert(bus);
assert(m); assert(m);
assert(c); assert(c);
r = sd_bus_message_read(m, "sb", &z, &interactive); r = sd_bus_message_read(m, "sb", &z, &interactive);
if (r < 0) if (r < 0)
return r; return r;
if (!valid_timezone(z)) if (!timezone_is_valid(z))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid time zone '%s'", z); return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid time zone '%s'", z);
if (streq_ptr(z, c->zone)) if (streq_ptr(z, c->zone))
return sd_bus_reply_method_return(m, NULL); return sd_bus_reply_method_return(m, NULL);
r = bus_verify_polkit_async(bus, &c->polkit_registry, m, "org.freed esktop.timedate1.set-timezone", interactive, error, method_set_timezone, c) ; r = bus_verify_polkit_async(m, CAP_SYS_TIME, "org.freedesktop.timed ate1.set-timezone", interactive, &c->polkit_registry, error);
if (r < 0) if (r < 0)
return r; return r;
if (r == 0) if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
t = strdup(z); t = strdup(z);
if (!t) if (!t)
return -ENOMEM; return -ENOMEM;
free(c->zone); free(c->zone);
skipping to change at line 572 skipping to change at line 459
assert(m); assert(m);
assert(c); assert(c);
r = sd_bus_message_read(m, "bbb", &lrtc, &fix_system, &interactive) ; r = sd_bus_message_read(m, "bbb", &lrtc, &fix_system, &interactive) ;
if (r < 0) if (r < 0)
return r; return r;
if (lrtc == c->local_rtc) if (lrtc == c->local_rtc)
return sd_bus_reply_method_return(m, NULL); return sd_bus_reply_method_return(m, NULL);
r = bus_verify_polkit_async(bus, &c->polkit_registry, m, "org.freed esktop.timedate1.set-local-rtc", interactive, error, method_set_local_rtc, c); r = bus_verify_polkit_async(m, CAP_SYS_TIME, "org.freedesktop.timed ate1.set-local-rtc", interactive, &c->polkit_registry, error);
if (r < 0) if (r < 0)
return r; return r;
if (r == 0) if (r == 0)
return 1; return 1;
c->local_rtc = lrtc; c->local_rtc = lrtc;
/* 1. Write new configuration file */ /* 1. Write new configuration file */
r = context_write_data_local_rtc(c); r = context_write_data_local_rtc(c);
if (r < 0) { if (r < 0) {
skipping to change at line 677 skipping to change at line 564
x = n + utc; x = n + utc;
if ((utc > 0 && x < n) || if ((utc > 0 && x < n) ||
(utc < 0 && x > n)) (utc < 0 && x > n))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALI D_ARGS, "Time value overflow"); return sd_bus_error_setf(error, SD_BUS_ERROR_INVALI D_ARGS, "Time value overflow");
timespec_store(&ts, x); timespec_store(&ts, x);
} else } else
timespec_store(&ts, (usec_t) utc); timespec_store(&ts, (usec_t) utc);
r = bus_verify_polkit_async(bus, &c->polkit_registry, m, "org.freed esktop.timedate1.set-time", interactive, error, method_set_time, c); r = bus_verify_polkit_async(m, CAP_SYS_TIME, "org.freedesktop.timed ate1.set-time", interactive, &c->polkit_registry, error);
if (r < 0) if (r < 0)
return r; return r;
if (r == 0) if (r == 0)
return 1; return 1;
/* Set system clock */ /* Set system clock */
if (clock_settime(CLOCK_REALTIME, &ts) < 0) { if (clock_settime(CLOCK_REALTIME, &ts) < 0) {
log_error("Failed to set local time: %m"); log_error("Failed to set local time: %m");
return sd_bus_error_set_errnof(error, errno, "Failed to set local time: %m"); return sd_bus_error_set_errnof(error, errno, "Failed to set local time: %m");
} }
skipping to change at line 717 skipping to change at line 604
Context *c = userdata; Context *c = userdata;
int r; int r;
r = sd_bus_message_read(m, "bb", &ntp, &interactive); r = sd_bus_message_read(m, "bb", &ntp, &interactive);
if (r < 0) if (r < 0)
return r; return r;
if ((bool)ntp == c->use_ntp) if ((bool)ntp == c->use_ntp)
return sd_bus_reply_method_return(m, NULL); return sd_bus_reply_method_return(m, NULL);
r = bus_verify_polkit_async(bus, &c->polkit_registry, m, "org.freed esktop.timedate1.set-ntp", interactive, error, method_set_ntp, c); r = bus_verify_polkit_async(m, CAP_SYS_TIME, "org.freedesktop.timed ate1.set-ntp", interactive, &c->polkit_registry, error);
if (r < 0) if (r < 0)
return r; return r;
if (r == 0) if (r == 0)
return 1; return 1;
c->use_ntp = ntp; c->use_ntp = ntp;
r = context_enable_ntp(c, bus, error); r = context_enable_ntp(c, bus, error);
if (r < 0) if (r < 0)
return r; return r;
skipping to change at line 740 skipping to change at line 627
if (r < 0) if (r < 0)
return r; return r;
log_info("Set NTP to %s", c->use_ntp ? "enabled" : "disabled"); log_info("Set NTP to %s", c->use_ntp ? "enabled" : "disabled");
sd_bus_emit_properties_changed(bus, "/org/freedesktop/timedate1", " org.freedesktop.timedate1", "NTP", NULL); sd_bus_emit_properties_changed(bus, "/org/freedesktop/timedate1", " org.freedesktop.timedate1", "NTP", NULL);
return sd_bus_reply_method_return(m, NULL); return sd_bus_reply_method_return(m, NULL);
} }
#include <sys/capability.h>
static const sd_bus_vtable timedate_vtable[] = { static const sd_bus_vtable timedate_vtable[] = {
SD_BUS_VTABLE_START(0), SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Timezone", "s", NULL, offsetof(Context, zone), SD_ BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("Timezone", "s", NULL, offsetof(Context, zone), SD_ BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("LocalRTC", "b", bus_property_get_bool, offsetof(Co ntext, local_rtc), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("LocalRTC", "b", bus_property_get_bool, offsetof(Co ntext, local_rtc), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("CanNTP", "b", bus_property_get_bool, offsetof(Cont ext, can_ntp), 0), SD_BUS_PROPERTY("CanNTP", "b", bus_property_get_bool, offsetof(Cont ext, can_ntp), 0),
SD_BUS_PROPERTY("NTP", "b", bus_property_get_bool, offsetof(Context , use_ntp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("NTP", "b", bus_property_get_bool, offsetof(Context , use_ntp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("NTPSynchronized", "b", property_get_ntp_sync, 0, 0 ), SD_BUS_PROPERTY("NTPSynchronized", "b", property_get_ntp_sync, 0, 0 ),
SD_BUS_PROPERTY("TimeUSec", "t", property_get_time, 0, 0), SD_BUS_PROPERTY("TimeUSec", "t", property_get_time, 0, 0),
SD_BUS_PROPERTY("RTCTimeUSec", "t", property_get_rtc_time, 0, 0), SD_BUS_PROPERTY("RTCTimeUSec", "t", property_get_rtc_time, 0, 0),
SD_BUS_METHOD("SetTime", "xbb", NULL, method_set_time, SD_BUS_VTABL E_UNPRIVILEGED), SD_BUS_METHOD("SetTime", "xbb", NULL, method_set_time, SD_BUS_VTABL E_UNPRIVILEGED),
SD_BUS_METHOD("SetTimezone", "sb", NULL, method_set_timezone, SD_BU S_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetTimezone", "sb", NULL, method_set_timezone, SD_BU S_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetLocalRTC", "bbb", NULL, method_set_local_rtc, SD_ BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetLocalRTC", "bbb", NULL, method_set_local_rtc, SD_ BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetNTP", "bb", NULL, method_set_ntp, SD_BUS_VTABLE_U NPRIVILEGED), SD_BUS_METHOD("SetNTP", "bb", NULL, method_set_ntp, SD_BUS_VTABLE_U NPRIVILEGED),
SD_BUS_VTABLE_END, SD_BUS_VTABLE_END,
}; };
static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) { static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
_cleanup_bus_unref_ sd_bus *bus = NULL; _cleanup_bus_close_unref_ sd_bus *bus = NULL;
int r; int r;
assert(c); assert(c);
assert(event); assert(event);
assert(_bus); assert(_bus);
r = sd_bus_default_system(&bus); r = sd_bus_default_system(&bus);
if (r < 0) { if (r < 0) {
log_error("Failed to get system bus connection: %s", strerr or(-r)); log_error("Failed to get system bus connection: %s", strerr or(-r));
return r; return r;
skipping to change at line 799 skipping to change at line 684
*_bus = bus; *_bus = bus;
bus = NULL; bus = NULL;
return 0; return 0;
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
Context context = {}; Context context = {};
_cleanup_event_unref_ sd_event *event = NULL; _cleanup_event_unref_ sd_event *event = NULL;
_cleanup_bus_unref_ sd_bus *bus = NULL; _cleanup_bus_close_unref_ sd_bus *bus = NULL;
int r; int r;
log_set_target(LOG_TARGET_AUTO); log_set_target(LOG_TARGET_AUTO);
log_parse_environment(); log_parse_environment();
log_open(); log_open();
umask(0022); umask(0022);
if (argc != 1) { if (argc != 1) {
log_error("This program takes no arguments."); log_error("This program takes no arguments.");
skipping to change at line 845 skipping to change at line 730
goto finish; goto finish;
} }
r = bus_event_loop_with_idle(event, bus, "org.freedesktop.timedate1 ", DEFAULT_EXIT_USEC, NULL, NULL); r = bus_event_loop_with_idle(event, bus, "org.freedesktop.timedate1 ", DEFAULT_EXIT_USEC, NULL, NULL);
if (r < 0) { if (r < 0) {
log_error("Failed to run event loop: %s", strerror(-r)); log_error("Failed to run event loop: %s", strerror(-r));
goto finish; goto finish;
} }
finish: finish:
context_free(&context, bus); context_free(&context);
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
} }
 End of changes. 30 change blocks. 
232 lines changed or deleted 114 lines changed or added

This html diff was produced by rfcdiff 1.41. The latest version is available from http://tools.ietf.org/tools/rfcdiff/