Remove code that has become unnecessary after sysupgrade changes
[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 "watchdog.h"
32
33 static struct blob_buf b;
34 static int notify;
35 static struct ubus_context *_ctx;
36
37 static int system_board(struct ubus_context *ctx, struct ubus_object *obj,
38                  struct ubus_request_data *req, const char *method,
39                  struct blob_attr *msg)
40 {
41         void *c;
42         char line[256];
43         char *key, *val, *next;
44         struct utsname utsname;
45         FILE *f;
46
47         blob_buf_init(&b, 0);
48
49         if (uname(&utsname) >= 0)
50         {
51                 blobmsg_add_string(&b, "kernel", utsname.release);
52                 blobmsg_add_string(&b, "hostname", utsname.nodename);
53         }
54
55         if ((f = fopen("/proc/cpuinfo", "r")) != NULL)
56         {
57                 while(fgets(line, sizeof(line), f))
58                 {
59                         key = strtok(line, "\t:");
60                         val = strtok(NULL, "\t\n");
61
62                         if (!key || !val)
63                                 continue;
64
65                         if (!strcasecmp(key, "system type") ||
66                             !strcasecmp(key, "processor") ||
67                             !strcasecmp(key, "model name"))
68                         {
69                                 strtoul(val + 2, &key, 0);
70
71                                 if (key == (val + 2) || *key != 0)
72                                 {
73                                         blobmsg_add_string(&b, "system", val + 2);
74                                         break;
75                                 }
76                         }
77                 }
78
79                 fclose(f);
80         }
81
82         if ((f = fopen("/tmp/sysinfo/model", "r")) != NULL ||
83             (f = fopen("/proc/device-tree/model", "r")) != NULL)
84         {
85                 if (fgets(line, sizeof(line), f))
86                 {
87                         val = strtok(line, "\t\n");
88
89                         if (val)
90                                 blobmsg_add_string(&b, "model", val);
91                 }
92
93                 fclose(f);
94         }
95         else if ((f = fopen("/proc/cpuinfo", "r")) != NULL)
96         {
97                 while(fgets(line, sizeof(line), f))
98                 {
99                         key = strtok(line, "\t:");
100                         val = strtok(NULL, "\t\n");
101
102                         if (!key || !val)
103                                 continue;
104
105                         if (!strcasecmp(key, "machine") ||
106                             !strcasecmp(key, "hardware"))
107                         {
108                                 blobmsg_add_string(&b, "model", val + 2);
109                                 break;
110                         }
111                 }
112
113                 fclose(f);
114         }
115
116         if ((f = fopen("/etc/openwrt_release", "r")) != NULL)
117         {
118                 c = blobmsg_open_table(&b, "release");
119
120                 while (fgets(line, sizeof(line), f))
121                 {
122                         char *dest;
123                         char ch;
124
125                         key = line;
126                         val = strchr(line, '=');
127                         if (!val)
128                                 continue;
129
130                         *(val++) = 0;
131
132                         if (!strcasecmp(key, "DISTRIB_ID"))
133                                 key = "distribution";
134                         else if (!strcasecmp(key, "DISTRIB_RELEASE"))
135                                 key = "version";
136                         else if (!strcasecmp(key, "DISTRIB_REVISION"))
137                                 key = "revision";
138                         else if (!strcasecmp(key, "DISTRIB_CODENAME"))
139                                 key = "codename";
140                         else if (!strcasecmp(key, "DISTRIB_TARGET"))
141                                 key = "target";
142                         else if (!strcasecmp(key, "DISTRIB_DESCRIPTION"))
143                                 key = "description";
144                         else
145                                 continue;
146
147                         dest = blobmsg_alloc_string_buffer(&b, key, strlen(val));
148                         if (!dest) {
149                                 ERROR("Failed to allocate blob.\n");
150                                 continue;
151                         }
152
153                         while (val && (ch = *(val++)) != 0) {
154                                 switch (ch) {
155                                 case '\'':
156                                 case '"':
157                                         next = strchr(val, ch);
158                                         if (next)
159                                                 *next = 0;
160
161                                         strcpy(dest, val);
162
163                                         if (next)
164                                                 val = next + 1;
165
166                                         dest += strlen(dest);
167                                         break;
168                                 case '\\':
169                                         *(dest++) = *(val++);
170                                         break;
171                                 }
172                         }
173                         blobmsg_add_string_buffer(&b);
174                 }
175
176                 blobmsg_close_array(&b, c);
177
178                 fclose(f);
179         }
180
181         ubus_send_reply(ctx, req, b.head);
182
183         return UBUS_STATUS_OK;
184 }
185
186 static int system_info(struct ubus_context *ctx, struct ubus_object *obj,
187                 struct ubus_request_data *req, const char *method,
188                 struct blob_attr *msg)
189 {
190         time_t now;
191         struct tm *tm;
192 #ifdef linux
193         struct sysinfo info;
194         void *c;
195
196         if (sysinfo(&info))
197                 return UBUS_STATUS_UNKNOWN_ERROR;
198 #endif
199
200         now = time(NULL);
201
202         if (!(tm = localtime(&now)))
203                 return UBUS_STATUS_UNKNOWN_ERROR;
204
205         blob_buf_init(&b, 0);
206
207         blobmsg_add_u32(&b, "localtime", now + tm->tm_gmtoff);
208
209 #ifdef linux
210         blobmsg_add_u32(&b, "uptime",    info.uptime);
211
212         c = blobmsg_open_array(&b, "load");
213         blobmsg_add_u32(&b, NULL, info.loads[0]);
214         blobmsg_add_u32(&b, NULL, info.loads[1]);
215         blobmsg_add_u32(&b, NULL, info.loads[2]);
216         blobmsg_close_array(&b, c);
217
218         c = blobmsg_open_table(&b, "memory");
219         blobmsg_add_u64(&b, "total",    info.mem_unit * info.totalram);
220         blobmsg_add_u64(&b, "free",     info.mem_unit * info.freeram);
221         blobmsg_add_u64(&b, "shared",   info.mem_unit * info.sharedram);
222         blobmsg_add_u64(&b, "buffered", info.mem_unit * info.bufferram);
223         blobmsg_close_table(&b, c);
224
225         c = blobmsg_open_table(&b, "swap");
226         blobmsg_add_u64(&b, "total",    info.mem_unit * info.totalswap);
227         blobmsg_add_u64(&b, "free",     info.mem_unit * info.freeswap);
228         blobmsg_close_table(&b, c);
229 #endif
230
231         ubus_send_reply(ctx, req, b.head);
232
233         return UBUS_STATUS_OK;
234 }
235
236 static int system_reboot(struct ubus_context *ctx, struct ubus_object *obj,
237                          struct ubus_request_data *req, const char *method,
238                          struct blob_attr *msg)
239 {
240         procd_shutdown(RB_AUTOBOOT);
241         return 0;
242 }
243
244 enum {
245         WDT_FREQUENCY,
246         WDT_TIMEOUT,
247         WDT_STOP,
248         __WDT_MAX
249 };
250
251 static const struct blobmsg_policy watchdog_policy[__WDT_MAX] = {
252         [WDT_FREQUENCY] = { .name = "frequency", .type = BLOBMSG_TYPE_INT32 },
253         [WDT_TIMEOUT] = { .name = "timeout", .type = BLOBMSG_TYPE_INT32 },
254         [WDT_STOP] = { .name = "stop", .type = BLOBMSG_TYPE_BOOL },
255 };
256
257 static int watchdog_set(struct ubus_context *ctx, struct ubus_object *obj,
258                         struct ubus_request_data *req, const char *method,
259                         struct blob_attr *msg)
260 {
261         struct blob_attr *tb[__WDT_MAX];
262         const char *status;
263
264         if (!msg)
265                 return UBUS_STATUS_INVALID_ARGUMENT;
266
267         blobmsg_parse(watchdog_policy, __WDT_MAX, tb, blob_data(msg), blob_len(msg));
268         if (tb[WDT_FREQUENCY]) {
269                 unsigned int timeout = watchdog_timeout(0);
270                 unsigned int freq = blobmsg_get_u32(tb[WDT_FREQUENCY]);
271
272                 if (freq) {
273                         if (freq > timeout / 2)
274                                 freq = timeout / 2;
275                         watchdog_frequency(freq);
276                 }
277         }
278
279         if (tb[WDT_TIMEOUT]) {
280                 unsigned int timeout = blobmsg_get_u32(tb[WDT_TIMEOUT]);
281                 unsigned int frequency = watchdog_frequency(0);
282
283                 if (timeout <= frequency)
284                         timeout = frequency * 2;
285                  watchdog_timeout(timeout);
286         }
287
288         if (tb[WDT_STOP])
289                 watchdog_set_stopped(blobmsg_get_bool(tb[WDT_STOP]));
290
291         if (watchdog_fd() == NULL)
292                 status = "offline";
293         else if (watchdog_get_stopped())
294                 status = "stopped";
295         else
296                 status = "running";
297
298         blob_buf_init(&b, 0);
299         blobmsg_add_string(&b, "status", status);
300         blobmsg_add_u32(&b, "timeout", watchdog_timeout(0));
301         blobmsg_add_u32(&b, "frequency", watchdog_frequency(0));
302         ubus_send_reply(ctx, req, b.head);
303
304         return 0;
305 }
306
307 enum {
308         SIGNAL_PID,
309         SIGNAL_NUM,
310         __SIGNAL_MAX
311 };
312
313 static const struct blobmsg_policy signal_policy[__SIGNAL_MAX] = {
314         [SIGNAL_PID] = { .name = "pid", .type = BLOBMSG_TYPE_INT32 },
315         [SIGNAL_NUM] = { .name = "signum", .type = BLOBMSG_TYPE_INT32 },
316 };
317
318 static int proc_signal(struct ubus_context *ctx, struct ubus_object *obj,
319                         struct ubus_request_data *req, const char *method,
320                         struct blob_attr *msg)
321 {
322         struct blob_attr *tb[__SIGNAL_MAX];
323
324         if (!msg)
325                 return UBUS_STATUS_INVALID_ARGUMENT;
326
327         blobmsg_parse(signal_policy, __SIGNAL_MAX, tb, blob_data(msg), blob_len(msg));
328         if (!tb[SIGNAL_PID || !tb[SIGNAL_NUM]])
329                 return UBUS_STATUS_INVALID_ARGUMENT;
330
331         kill(blobmsg_get_u32(tb[SIGNAL_PID]), blobmsg_get_u32(tb[SIGNAL_NUM]));
332
333         return 0;
334 }
335
336 enum {
337         SYSUPGRADE_PATH,
338         SYSUPGRADE_PREFIX,
339         SYSUPGRADE_COMMAND,
340         __SYSUPGRADE_MAX
341 };
342
343 static const struct blobmsg_policy sysupgrade_policy[__SYSUPGRADE_MAX] = {
344         [SYSUPGRADE_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
345         [SYSUPGRADE_PREFIX] = { .name = "prefix", .type = BLOBMSG_TYPE_STRING },
346         [SYSUPGRADE_COMMAND] = { .name = "command", .type = BLOBMSG_TYPE_STRING },
347 };
348
349 static void
350 procd_exec_upgraded(const char *prefix, char *path, char *command)
351 {
352         char *wdt_fd = watchdog_fd();
353         char *argv[] = { "/sbin/upgraded", NULL, NULL, NULL};
354
355         if (chroot(prefix)) {
356                 fprintf(stderr, "Failed to chroot for upgraded exec.\n");
357                 return;
358         }
359
360         argv[1] = path;
361         argv[2] = command;
362
363         DEBUG(2, "Exec to upgraded now\n");
364         if (wdt_fd) {
365                 watchdog_set_cloexec(false);
366                 setenv("WDTFD", wdt_fd, 1);
367         }
368         execvp(argv[0], argv);
369
370         /* Cleanup on failure */
371         fprintf(stderr, "Failed to exec upgraded.\n");
372         unsetenv("WDTFD");
373         watchdog_set_cloexec(true);
374         chroot(".");
375 }
376
377 static int sysupgrade(struct ubus_context *ctx, struct ubus_object *obj,
378                       struct ubus_request_data *req, const char *method,
379                       struct blob_attr *msg)
380 {
381         struct blob_attr *tb[__SYSUPGRADE_MAX];
382
383         if (!msg)
384                 return UBUS_STATUS_INVALID_ARGUMENT;
385
386         blobmsg_parse(sysupgrade_policy, __SYSUPGRADE_MAX, tb, blob_data(msg), blob_len(msg));
387         if (!tb[SYSUPGRADE_PATH] || !tb[SYSUPGRADE_PREFIX])
388                 return UBUS_STATUS_INVALID_ARGUMENT;
389
390         procd_exec_upgraded(blobmsg_get_string(tb[SYSUPGRADE_PREFIX]),
391                             blobmsg_get_string(tb[SYSUPGRADE_PATH]),
392                             tb[SYSUPGRADE_COMMAND] ? blobmsg_get_string(tb[SYSUPGRADE_COMMAND]) : NULL);
393         return 0;
394 }
395
396 static void
397 procd_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj)
398 {
399         notify = obj->has_subscribers;
400 }
401
402
403 static const struct ubus_method system_methods[] = {
404         UBUS_METHOD_NOARG("board", system_board),
405         UBUS_METHOD_NOARG("info",  system_info),
406         UBUS_METHOD_NOARG("reboot", system_reboot),
407         UBUS_METHOD("watchdog", watchdog_set, watchdog_policy),
408         UBUS_METHOD("signal", proc_signal, signal_policy),
409         UBUS_METHOD("sysupgrade", sysupgrade, sysupgrade_policy),
410 };
411
412 static struct ubus_object_type system_object_type =
413         UBUS_OBJECT_TYPE("system", system_methods);
414
415 static struct ubus_object system_object = {
416         .name = "system",
417         .type = &system_object_type,
418         .methods = system_methods,
419         .n_methods = ARRAY_SIZE(system_methods),
420         .subscribe_cb = procd_subscribe_cb,
421 };
422
423 void
424 procd_bcast_event(char *event, struct blob_attr *msg)
425 {
426         int ret;
427
428         if (!notify)
429                 return;
430
431         ret = ubus_notify(_ctx, &system_object, event, msg, -1);
432         if (ret)
433                 fprintf(stderr, "Failed to notify log: %s\n", ubus_strerror(ret));
434 }
435
436 void ubus_init_system(struct ubus_context *ctx)
437 {
438         int ret;
439
440         _ctx = ctx;
441         ret = ubus_add_object(ctx, &system_object);
442         if (ret)
443                 ERROR("Failed to add object: %s\n", ubus_strerror(ret));
444 }