init: add support for sysupgrades triggered from preinit
[project/procd.git] / system.c
1 /*
2  * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
3  * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License version 2.1
7  * as published by the Free Software Foundation
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14
15 #include <sys/utsname.h>
16 #ifdef linux
17 #include <sys/sysinfo.h>
18 #endif
19 #include <sys/ioctl.h>
20 #include <sys/types.h>
21 #include <sys/reboot.h>
22 #include <sys/stat.h>
23 #include <fcntl.h>
24 #include <signal.h>
25 #include <unistd.h>
26 #include <stdlib.h>
27
28 #include <libubox/uloop.h>
29
30 #include "procd.h"
31 #include "sysupgrade.h"
32 #include "watchdog.h"
33
34 static struct blob_buf b;
35 static int notify;
36 static struct ubus_context *_ctx;
37
38 static int system_board(struct ubus_context *ctx, struct ubus_object *obj,
39                  struct ubus_request_data *req, const char *method,
40                  struct blob_attr *msg)
41 {
42         void *c;
43         char line[256];
44         char *key, *val, *next;
45         struct utsname utsname;
46         FILE *f;
47
48         blob_buf_init(&b, 0);
49
50         if (uname(&utsname) >= 0)
51         {
52                 blobmsg_add_string(&b, "kernel", utsname.release);
53                 blobmsg_add_string(&b, "hostname", utsname.nodename);
54         }
55
56         if ((f = fopen("/proc/cpuinfo", "r")) != NULL)
57         {
58                 while(fgets(line, sizeof(line), f))
59                 {
60                         key = strtok(line, "\t:");
61                         val = strtok(NULL, "\t\n");
62
63                         if (!key || !val)
64                                 continue;
65
66                         if (!strcasecmp(key, "system type") ||
67                             !strcasecmp(key, "processor") ||
68                             !strcasecmp(key, "model name"))
69                         {
70                                 strtoul(val + 2, &key, 0);
71
72                                 if (key == (val + 2) || *key != 0)
73                                 {
74                                         blobmsg_add_string(&b, "system", val + 2);
75                                         break;
76                                 }
77                         }
78                 }
79
80                 fclose(f);
81         }
82
83         if ((f = fopen("/tmp/sysinfo/model", "r")) != NULL ||
84             (f = fopen("/proc/device-tree/model", "r")) != NULL)
85         {
86                 if (fgets(line, sizeof(line), f))
87                 {
88                         val = strtok(line, "\t\n");
89
90                         if (val)
91                                 blobmsg_add_string(&b, "model", val);
92                 }
93
94                 fclose(f);
95         }
96         else if ((f = fopen("/proc/cpuinfo", "r")) != NULL)
97         {
98                 while(fgets(line, sizeof(line), f))
99                 {
100                         key = strtok(line, "\t:");
101                         val = strtok(NULL, "\t\n");
102
103                         if (!key || !val)
104                                 continue;
105
106                         if (!strcasecmp(key, "machine") ||
107                             !strcasecmp(key, "hardware"))
108                         {
109                                 blobmsg_add_string(&b, "model", val + 2);
110                                 break;
111                         }
112                 }
113
114                 fclose(f);
115         }
116
117         if ((f = fopen("/etc/openwrt_release", "r")) != NULL)
118         {
119                 c = blobmsg_open_table(&b, "release");
120
121                 while (fgets(line, sizeof(line), f))
122                 {
123                         char *dest;
124                         char ch;
125
126                         key = line;
127                         val = strchr(line, '=');
128                         if (!val)
129                                 continue;
130
131                         *(val++) = 0;
132
133                         if (!strcasecmp(key, "DISTRIB_ID"))
134                                 key = "distribution";
135                         else if (!strcasecmp(key, "DISTRIB_RELEASE"))
136                                 key = "version";
137                         else if (!strcasecmp(key, "DISTRIB_REVISION"))
138                                 key = "revision";
139                         else if (!strcasecmp(key, "DISTRIB_CODENAME"))
140                                 key = "codename";
141                         else if (!strcasecmp(key, "DISTRIB_TARGET"))
142                                 key = "target";
143                         else if (!strcasecmp(key, "DISTRIB_DESCRIPTION"))
144                                 key = "description";
145                         else
146                                 continue;
147
148                         dest = blobmsg_alloc_string_buffer(&b, key, strlen(val));
149                         if (!dest) {
150                                 ERROR("Failed to allocate blob.\n");
151                                 continue;
152                         }
153
154                         while (val && (ch = *(val++)) != 0) {
155                                 switch (ch) {
156                                 case '\'':
157                                 case '"':
158                                         next = strchr(val, ch);
159                                         if (next)
160                                                 *next = 0;
161
162                                         strcpy(dest, val);
163
164                                         if (next)
165                                                 val = next + 1;
166
167                                         dest += strlen(dest);
168                                         break;
169                                 case '\\':
170                                         *(dest++) = *(val++);
171                                         break;
172                                 }
173                         }
174                         blobmsg_add_string_buffer(&b);
175                 }
176
177                 blobmsg_close_array(&b, c);
178
179                 fclose(f);
180         }
181
182         ubus_send_reply(ctx, req, b.head);
183
184         return UBUS_STATUS_OK;
185 }
186
187 static int system_info(struct ubus_context *ctx, struct ubus_object *obj,
188                 struct ubus_request_data *req, const char *method,
189                 struct blob_attr *msg)
190 {
191         time_t now;
192         struct tm *tm;
193 #ifdef linux
194         struct sysinfo info;
195         void *c;
196
197         if (sysinfo(&info))
198                 return UBUS_STATUS_UNKNOWN_ERROR;
199 #endif
200
201         now = time(NULL);
202
203         if (!(tm = localtime(&now)))
204                 return UBUS_STATUS_UNKNOWN_ERROR;
205
206         blob_buf_init(&b, 0);
207
208         blobmsg_add_u32(&b, "localtime", now + tm->tm_gmtoff);
209
210 #ifdef linux
211         blobmsg_add_u32(&b, "uptime",    info.uptime);
212
213         c = blobmsg_open_array(&b, "load");
214         blobmsg_add_u32(&b, NULL, info.loads[0]);
215         blobmsg_add_u32(&b, NULL, info.loads[1]);
216         blobmsg_add_u32(&b, NULL, info.loads[2]);
217         blobmsg_close_array(&b, c);
218
219         c = blobmsg_open_table(&b, "memory");
220         blobmsg_add_u64(&b, "total",    info.mem_unit * info.totalram);
221         blobmsg_add_u64(&b, "free",     info.mem_unit * info.freeram);
222         blobmsg_add_u64(&b, "shared",   info.mem_unit * info.sharedram);
223         blobmsg_add_u64(&b, "buffered", info.mem_unit * info.bufferram);
224         blobmsg_close_table(&b, c);
225
226         c = blobmsg_open_table(&b, "swap");
227         blobmsg_add_u64(&b, "total",    info.mem_unit * info.totalswap);
228         blobmsg_add_u64(&b, "free",     info.mem_unit * info.freeswap);
229         blobmsg_close_table(&b, c);
230 #endif
231
232         ubus_send_reply(ctx, req, b.head);
233
234         return UBUS_STATUS_OK;
235 }
236
237 static int system_reboot(struct ubus_context *ctx, struct ubus_object *obj,
238                          struct ubus_request_data *req, const char *method,
239                          struct blob_attr *msg)
240 {
241         procd_shutdown(RB_AUTOBOOT);
242         return 0;
243 }
244
245 enum {
246         WDT_FREQUENCY,
247         WDT_TIMEOUT,
248         WDT_STOP,
249         __WDT_MAX
250 };
251
252 static const struct blobmsg_policy watchdog_policy[__WDT_MAX] = {
253         [WDT_FREQUENCY] = { .name = "frequency", .type = BLOBMSG_TYPE_INT32 },
254         [WDT_TIMEOUT] = { .name = "timeout", .type = BLOBMSG_TYPE_INT32 },
255         [WDT_STOP] = { .name = "stop", .type = BLOBMSG_TYPE_BOOL },
256 };
257
258 static int watchdog_set(struct ubus_context *ctx, struct ubus_object *obj,
259                         struct ubus_request_data *req, const char *method,
260                         struct blob_attr *msg)
261 {
262         struct blob_attr *tb[__WDT_MAX];
263         const char *status;
264
265         if (!msg)
266                 return UBUS_STATUS_INVALID_ARGUMENT;
267
268         blobmsg_parse(watchdog_policy, __WDT_MAX, tb, blob_data(msg), blob_len(msg));
269         if (tb[WDT_FREQUENCY]) {
270                 unsigned int timeout = watchdog_timeout(0);
271                 unsigned int freq = blobmsg_get_u32(tb[WDT_FREQUENCY]);
272
273                 if (freq) {
274                         if (freq > timeout / 2)
275                                 freq = timeout / 2;
276                         watchdog_frequency(freq);
277                 }
278         }
279
280         if (tb[WDT_TIMEOUT]) {
281                 unsigned int timeout = blobmsg_get_u32(tb[WDT_TIMEOUT]);
282                 unsigned int frequency = watchdog_frequency(0);
283
284                 if (timeout <= frequency)
285                         timeout = frequency * 2;
286                  watchdog_timeout(timeout);
287         }
288
289         if (tb[WDT_STOP])
290                 watchdog_set_stopped(blobmsg_get_bool(tb[WDT_STOP]));
291
292         if (watchdog_fd() == NULL)
293                 status = "offline";
294         else if (watchdog_get_stopped())
295                 status = "stopped";
296         else
297                 status = "running";
298
299         blob_buf_init(&b, 0);
300         blobmsg_add_string(&b, "status", status);
301         blobmsg_add_u32(&b, "timeout", watchdog_timeout(0));
302         blobmsg_add_u32(&b, "frequency", watchdog_frequency(0));
303         ubus_send_reply(ctx, req, b.head);
304
305         return 0;
306 }
307
308 enum {
309         SIGNAL_PID,
310         SIGNAL_NUM,
311         __SIGNAL_MAX
312 };
313
314 static const struct blobmsg_policy signal_policy[__SIGNAL_MAX] = {
315         [SIGNAL_PID] = { .name = "pid", .type = BLOBMSG_TYPE_INT32 },
316         [SIGNAL_NUM] = { .name = "signum", .type = BLOBMSG_TYPE_INT32 },
317 };
318
319 static int proc_signal(struct ubus_context *ctx, struct ubus_object *obj,
320                         struct ubus_request_data *req, const char *method,
321                         struct blob_attr *msg)
322 {
323         struct blob_attr *tb[__SIGNAL_MAX];
324
325         if (!msg)
326                 return UBUS_STATUS_INVALID_ARGUMENT;
327
328         blobmsg_parse(signal_policy, __SIGNAL_MAX, tb, blob_data(msg), blob_len(msg));
329         if (!tb[SIGNAL_PID || !tb[SIGNAL_NUM]])
330                 return UBUS_STATUS_INVALID_ARGUMENT;
331
332         kill(blobmsg_get_u32(tb[SIGNAL_PID]), blobmsg_get_u32(tb[SIGNAL_NUM]));
333
334         return 0;
335 }
336
337 enum {
338         SYSUPGRADE_PATH,
339         SYSUPGRADE_PREFIX,
340         SYSUPGRADE_COMMAND,
341         __SYSUPGRADE_MAX
342 };
343
344 static const struct blobmsg_policy sysupgrade_policy[__SYSUPGRADE_MAX] = {
345         [SYSUPGRADE_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
346         [SYSUPGRADE_PREFIX] = { .name = "prefix", .type = BLOBMSG_TYPE_STRING },
347         [SYSUPGRADE_COMMAND] = { .name = "command", .type = BLOBMSG_TYPE_STRING },
348 };
349
350 static int sysupgrade(struct ubus_context *ctx, struct ubus_object *obj,
351                       struct ubus_request_data *req, const char *method,
352                       struct blob_attr *msg)
353 {
354         struct blob_attr *tb[__SYSUPGRADE_MAX];
355
356         if (!msg)
357                 return UBUS_STATUS_INVALID_ARGUMENT;
358
359         blobmsg_parse(sysupgrade_policy, __SYSUPGRADE_MAX, tb, blob_data(msg), blob_len(msg));
360         if (!tb[SYSUPGRADE_PATH] || !tb[SYSUPGRADE_PREFIX])
361                 return UBUS_STATUS_INVALID_ARGUMENT;
362
363         sysupgrade_exec_upgraded(blobmsg_get_string(tb[SYSUPGRADE_PREFIX]),
364                                  blobmsg_get_string(tb[SYSUPGRADE_PATH]),
365                                  tb[SYSUPGRADE_COMMAND] ? blobmsg_get_string(tb[SYSUPGRADE_COMMAND]) : NULL);
366         return 0;
367 }
368
369 static void
370 procd_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj)
371 {
372         notify = obj->has_subscribers;
373 }
374
375
376 static const struct ubus_method system_methods[] = {
377         UBUS_METHOD_NOARG("board", system_board),
378         UBUS_METHOD_NOARG("info",  system_info),
379         UBUS_METHOD_NOARG("reboot", system_reboot),
380         UBUS_METHOD("watchdog", watchdog_set, watchdog_policy),
381         UBUS_METHOD("signal", proc_signal, signal_policy),
382         UBUS_METHOD("sysupgrade", sysupgrade, sysupgrade_policy),
383 };
384
385 static struct ubus_object_type system_object_type =
386         UBUS_OBJECT_TYPE("system", system_methods);
387
388 static struct ubus_object system_object = {
389         .name = "system",
390         .type = &system_object_type,
391         .methods = system_methods,
392         .n_methods = ARRAY_SIZE(system_methods),
393         .subscribe_cb = procd_subscribe_cb,
394 };
395
396 void
397 procd_bcast_event(char *event, struct blob_attr *msg)
398 {
399         int ret;
400
401         if (!notify)
402                 return;
403
404         ret = ubus_notify(_ctx, &system_object, event, msg, -1);
405         if (ret)
406                 fprintf(stderr, "Failed to notify log: %s\n", ubus_strerror(ret));
407 }
408
409 void ubus_init_system(struct ubus_context *ctx)
410 {
411         int ret;
412
413         _ctx = ctx;
414         ret = ubus_add_object(ctx, &system_object);
415         if (ret)
416                 ERROR("Failed to add object: %s\n", ubus_strerror(ret));
417 }