utils: add patch_fd() and patch_stdio() helpers
[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/stat.h>
22 #include <fcntl.h>
23 #include <signal.h>
24 #include <unistd.h>
25 #include <stdlib.h>
26
27 #include <libubox/uloop.h>
28
29 #include "procd.h"
30 #include "watchdog.h"
31
32 static struct blob_buf b;
33 static int notify;
34 static struct ubus_context *_ctx;
35
36 int upgrade_running = 0;
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", mktime(tm));
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_upgrade(struct ubus_context *ctx, struct ubus_object *obj,
238                         struct ubus_request_data *req, const char *method,
239                         struct blob_attr *msg)
240 {
241         upgrade_running = 1;
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         NAND_PATH,
339         __NAND_MAX
340 };
341
342 static const struct blobmsg_policy nand_policy[__NAND_MAX] = {
343         [NAND_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
344 };
345
346 static void
347 procd_spawn_upgraded(char *path)
348 {
349         char *wdt_fd = watchdog_fd();
350         char *argv[] = { "/tmp/upgraded", NULL, NULL};
351
352         argv[1] = path;
353
354         DEBUG(2, "Exec to upgraded now\n");
355         if (wdt_fd) {
356                 watchdog_no_cloexec();
357                 setenv("WDTFD", wdt_fd, 1);
358         }
359         execvp(argv[0], argv);
360 }
361
362 static int nand_set(struct ubus_context *ctx, struct ubus_object *obj,
363                         struct ubus_request_data *req, const char *method,
364                         struct blob_attr *msg)
365 {
366         struct blob_attr *tb[__NAND_MAX];
367
368         if (!msg)
369                 return UBUS_STATUS_INVALID_ARGUMENT;
370
371         blobmsg_parse(nand_policy, __NAND_MAX, tb, blob_data(msg), blob_len(msg));
372         if (!tb[NAND_PATH])
373                 return UBUS_STATUS_INVALID_ARGUMENT;
374
375         procd_spawn_upgraded(blobmsg_get_string(tb[NAND_PATH]));
376         fprintf(stderr, "Yikees, something went wrong. no /sbin/upgraded ?\n");
377         return 0;
378 }
379
380 static void
381 procd_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj)
382 {
383         notify = obj->has_subscribers;
384 }
385
386
387 static const struct ubus_method system_methods[] = {
388         UBUS_METHOD_NOARG("board", system_board),
389         UBUS_METHOD_NOARG("info",  system_info),
390         UBUS_METHOD_NOARG("upgrade", system_upgrade),
391         UBUS_METHOD("watchdog", watchdog_set, watchdog_policy),
392         UBUS_METHOD("signal", proc_signal, signal_policy),
393
394         /* must remain at the end as it ia not always loaded */
395         UBUS_METHOD("nandupgrade", nand_set, nand_policy),
396 };
397
398 static struct ubus_object_type system_object_type =
399         UBUS_OBJECT_TYPE("system", system_methods);
400
401 static struct ubus_object system_object = {
402         .name = "system",
403         .type = &system_object_type,
404         .methods = system_methods,
405         .n_methods = ARRAY_SIZE(system_methods),
406         .subscribe_cb = procd_subscribe_cb,
407 };
408
409 void
410 procd_bcast_event(char *event, struct blob_attr *msg)
411 {
412         int ret;
413
414         if (!notify)
415                 return;
416
417         ret = ubus_notify(_ctx, &system_object, event, msg, -1);
418         if (ret)
419                 fprintf(stderr, "Failed to notify log: %s\n", ubus_strerror(ret));
420 }
421
422 void ubus_init_system(struct ubus_context *ctx)
423 {
424         struct stat s;
425         int ret;
426
427         if (stat("/sbin/upgraded", &s))
428                 system_object.n_methods -= 1;
429
430         _ctx = ctx;
431         ret = ubus_add_object(ctx, &system_object);
432         if (ret)
433                 ERROR("Failed to add object: %s\n", ubus_strerror(ret));
434 }