]>
Commit | Line | Data |
---|---|---|
d450bc50 | 1 | // SPDX-License-Identifier: GPL-2.0 |
ed36b704 | 2 | #include <internal/lib.h> |
d450bc50 | 3 | #include <subcmd/parse-options.h> |
ed36b704 | 4 | #include <api/fd/array.h> |
8c98be6c | 5 | #include <api/fs/fs.h> |
c0666261 JO |
6 | #include <linux/zalloc.h> |
7 | #include <linux/string.h> | |
d450bc50 JO |
8 | #include <linux/limits.h> |
9 | #include <string.h> | |
8c98be6c | 10 | #include <sys/file.h> |
d450bc50 | 11 | #include <signal.h> |
fc1dcb1e | 12 | #include <stdlib.h> |
12c1a415 | 13 | #include <time.h> |
d450bc50 JO |
14 | #include <stdio.h> |
15 | #include <unistd.h> | |
5631d100 | 16 | #include <errno.h> |
3cda0625 JO |
17 | #include <sys/inotify.h> |
18 | #include <libgen.h> | |
ed36b704 JO |
19 | #include <sys/types.h> |
20 | #include <sys/socket.h> | |
21 | #include <sys/un.h> | |
90b0aad8 | 22 | #include <sys/stat.h> |
12c1a415 JO |
23 | #include <sys/signalfd.h> |
24 | #include <sys/wait.h> | |
ed36b704 | 25 | #include <poll.h> |
d450bc50 JO |
26 | #include "builtin.h" |
27 | #include "perf.h" | |
28 | #include "debug.h" | |
fc1dcb1e | 29 | #include "config.h" |
d450bc50 JO |
30 | #include "util.h" |
31 | ||
c0666261 | 32 | #define SESSION_OUTPUT "output" |
6a6d1804 JO |
33 | #define SESSION_CONTROL "control" |
34 | #define SESSION_ACK "ack" | |
c0666261 JO |
35 | |
36 | /* | |
37 | * Session states: | |
38 | * | |
39 | * OK - session is up and running | |
40 | * RECONFIG - session is pending for reconfiguration, | |
41 | * new values are already loaded in session object | |
42 | * KILL - session is pending to be killed | |
43 | * | |
44 | * Session object life and its state is maintained by | |
45 | * following functions: | |
46 | * | |
47 | * setup_server_config | |
48 | * - reads config file and setup session objects | |
49 | * with following states: | |
50 | * | |
51 | * OK - no change needed | |
52 | * RECONFIG - session needs to be changed | |
53 | * (run variable changed) | |
54 | * KILL - session needs to be killed | |
55 | * (session is no longer in config file) | |
56 | * | |
57 | * daemon__reconfig | |
58 | * - scans session objects and does following actions | |
59 | * for states: | |
60 | * | |
61 | * OK - skip | |
62 | * RECONFIG - session is killed and re-run with new config | |
63 | * KILL - session is killed | |
64 | * | |
65 | * - all sessions have OK state on the function exit | |
66 | */ | |
67 | enum daemon_session_state { | |
68 | OK, | |
69 | RECONFIG, | |
70 | KILL, | |
71 | }; | |
72 | ||
73 | struct daemon_session { | |
74 | char *base; | |
75 | char *name; | |
76 | char *run; | |
6a6d1804 | 77 | char *control; |
c0666261 JO |
78 | int pid; |
79 | struct list_head list; | |
80 | enum daemon_session_state state; | |
5bdee4f0 | 81 | time_t start; |
c0666261 JO |
82 | }; |
83 | ||
d450bc50 | 84 | struct daemon { |
fc1dcb1e JO |
85 | const char *config; |
86 | char *config_real; | |
3cda0625 | 87 | char *config_base; |
b325f7be | 88 | const char *csv_sep; |
5631d100 | 89 | const char *base_user; |
d450bc50 | 90 | char *base; |
c0666261 | 91 | struct list_head sessions; |
d450bc50 JO |
92 | FILE *out; |
93 | char perf[PATH_MAX]; | |
12c1a415 | 94 | int signal_fd; |
5bdee4f0 | 95 | time_t start; |
d450bc50 JO |
96 | }; |
97 | ||
c0666261 JO |
98 | static struct daemon __daemon = { |
99 | .sessions = LIST_HEAD_INIT(__daemon.sessions), | |
100 | }; | |
d450bc50 JO |
101 | |
102 | static const char * const daemon_usage[] = { | |
103 | "perf daemon start [<options>]", | |
104 | "perf daemon [<options>]", | |
105 | NULL | |
106 | }; | |
107 | ||
108 | static bool done; | |
109 | ||
110 | static void sig_handler(int sig __maybe_unused) | |
111 | { | |
112 | done = true; | |
113 | } | |
114 | ||
c0666261 JO |
115 | static struct daemon_session *daemon__add_session(struct daemon *config, char *name) |
116 | { | |
117 | struct daemon_session *session = zalloc(sizeof(*session)); | |
118 | ||
119 | if (!session) | |
120 | return NULL; | |
121 | ||
122 | session->name = strdup(name); | |
123 | if (!session->name) { | |
124 | free(session); | |
125 | return NULL; | |
126 | } | |
127 | ||
128 | session->pid = -1; | |
129 | list_add_tail(&session->list, &config->sessions); | |
130 | return session; | |
131 | } | |
132 | ||
133 | static struct daemon_session *daemon__find_session(struct daemon *daemon, char *name) | |
134 | { | |
135 | struct daemon_session *session; | |
136 | ||
137 | list_for_each_entry(session, &daemon->sessions, list) { | |
138 | if (!strcmp(session->name, name)) | |
139 | return session; | |
140 | } | |
141 | ||
142 | return NULL; | |
143 | } | |
144 | ||
145 | static int get_session_name(const char *var, char *session, int len) | |
146 | { | |
147 | const char *p = var + sizeof("session-") - 1; | |
148 | ||
149 | while (*p != '.' && *p != 0x0 && len--) | |
150 | *session++ = *p++; | |
151 | ||
152 | *session = 0; | |
153 | return *p == '.' ? 0 : -EINVAL; | |
154 | } | |
155 | ||
156 | static int session_config(struct daemon *daemon, const char *var, const char *value) | |
157 | { | |
158 | struct daemon_session *session; | |
159 | char name[100]; | |
160 | ||
bd57a9f3 | 161 | if (get_session_name(var, name, sizeof(name) - 1)) |
c0666261 JO |
162 | return -EINVAL; |
163 | ||
164 | var = strchr(var, '.'); | |
165 | if (!var) | |
166 | return -EINVAL; | |
167 | ||
168 | var++; | |
169 | ||
170 | session = daemon__find_session(daemon, name); | |
171 | ||
172 | if (!session) { | |
173 | /* New session is defined. */ | |
174 | session = daemon__add_session(daemon, name); | |
175 | if (!session) | |
176 | return -ENOMEM; | |
177 | ||
178 | pr_debug("reconfig: found new session %s\n", name); | |
179 | ||
180 | /* Trigger reconfig to start it. */ | |
181 | session->state = RECONFIG; | |
182 | } else if (session->state == KILL) { | |
183 | /* Current session is defined, no action needed. */ | |
184 | pr_debug("reconfig: found current session %s\n", name); | |
185 | session->state = OK; | |
186 | } | |
187 | ||
188 | if (!strcmp(var, "run")) { | |
189 | bool same = false; | |
190 | ||
191 | if (session->run) | |
192 | same = !strcmp(session->run, value); | |
193 | ||
194 | if (!same) { | |
195 | if (session->run) { | |
196 | free(session->run); | |
197 | pr_debug("reconfig: session %s is changed\n", name); | |
198 | } | |
199 | ||
200 | session->run = strdup(value); | |
201 | if (!session->run) | |
202 | return -ENOMEM; | |
203 | ||
204 | /* | |
205 | * Either new or changed run value is defined, | |
206 | * trigger reconfig for the session. | |
207 | */ | |
208 | session->state = RECONFIG; | |
209 | } | |
210 | } | |
211 | ||
212 | return 0; | |
213 | } | |
214 | ||
215 | static int server_config(const char *var, const char *value, void *cb) | |
216 | { | |
217 | struct daemon *daemon = cb; | |
218 | ||
219 | if (strstarts(var, "session-")) { | |
220 | return session_config(daemon, var, value); | |
221 | } else if (!strcmp(var, "daemon.base") && !daemon->base_user) { | |
222 | if (daemon->base && strcmp(daemon->base, value)) { | |
223 | pr_err("failed: can't redefine base, bailing out\n"); | |
224 | return -EINVAL; | |
225 | } | |
226 | daemon->base = strdup(value); | |
227 | if (!daemon->base) | |
228 | return -ENOMEM; | |
229 | } | |
230 | ||
231 | return 0; | |
232 | } | |
233 | ||
90b0aad8 JO |
234 | static int client_config(const char *var, const char *value, void *cb) |
235 | { | |
236 | struct daemon *daemon = cb; | |
237 | ||
238 | if (!strcmp(var, "daemon.base") && !daemon->base_user) { | |
239 | daemon->base = strdup(value); | |
240 | if (!daemon->base) | |
241 | return -ENOMEM; | |
242 | } | |
243 | ||
244 | return 0; | |
245 | } | |
246 | ||
247 | static int check_base(struct daemon *daemon) | |
248 | { | |
249 | struct stat st; | |
250 | ||
251 | if (!daemon->base) { | |
252 | pr_err("failed: base not defined\n"); | |
253 | return -EINVAL; | |
254 | } | |
255 | ||
256 | if (stat(daemon->base, &st)) { | |
257 | switch (errno) { | |
258 | case EACCES: | |
259 | pr_err("failed: permission denied for '%s' base\n", | |
260 | daemon->base); | |
261 | return -EACCES; | |
262 | case ENOENT: | |
263 | pr_err("failed: base '%s' does not exists\n", | |
264 | daemon->base); | |
265 | return -EACCES; | |
266 | default: | |
267 | pr_err("failed: can't access base '%s': %s\n", | |
268 | daemon->base, strerror(errno)); | |
269 | return -errno; | |
270 | } | |
271 | } | |
272 | ||
273 | if ((st.st_mode & S_IFMT) != S_IFDIR) { | |
274 | pr_err("failed: base '%s' is not directory\n", | |
275 | daemon->base); | |
276 | return -EINVAL; | |
277 | } | |
278 | ||
279 | return 0; | |
280 | } | |
281 | ||
282 | static int setup_client_config(struct daemon *daemon) | |
283 | { | |
284 | struct perf_config_set *set = perf_config_set__load_file(daemon->config_real); | |
285 | int err = -ENOMEM; | |
286 | ||
287 | if (set) { | |
288 | err = perf_config_set(set, client_config, daemon); | |
289 | perf_config_set__delete(set); | |
290 | } | |
291 | ||
292 | return err ?: check_base(daemon); | |
293 | } | |
294 | ||
c0666261 JO |
295 | static int setup_server_config(struct daemon *daemon) |
296 | { | |
297 | struct perf_config_set *set; | |
298 | struct daemon_session *session; | |
299 | int err = -ENOMEM; | |
300 | ||
301 | pr_debug("reconfig: started\n"); | |
302 | ||
303 | /* | |
304 | * Mark all sessions for kill, the server config | |
305 | * will set following states, see explanation at | |
306 | * enum daemon_session_state declaration. | |
307 | */ | |
308 | list_for_each_entry(session, &daemon->sessions, list) | |
309 | session->state = KILL; | |
310 | ||
311 | set = perf_config_set__load_file(daemon->config_real); | |
312 | if (set) { | |
313 | err = perf_config_set(set, server_config, daemon); | |
314 | perf_config_set__delete(set); | |
315 | } | |
316 | ||
317 | return err ?: check_base(daemon); | |
318 | } | |
319 | ||
320 | static int daemon_session__run(struct daemon_session *session, | |
321 | struct daemon *daemon) | |
322 | { | |
323 | char buf[PATH_MAX]; | |
324 | char **argv; | |
325 | int argc, fd; | |
326 | ||
327 | if (asprintf(&session->base, "%s/session-%s", | |
328 | daemon->base, session->name) < 0) { | |
329 | perror("failed: asprintf"); | |
330 | return -1; | |
331 | } | |
332 | ||
333 | if (mkdir(session->base, 0755) && errno != EEXIST) { | |
334 | perror("failed: mkdir"); | |
335 | return -1; | |
336 | } | |
337 | ||
5bdee4f0 JO |
338 | session->start = time(NULL); |
339 | ||
c0666261 JO |
340 | session->pid = fork(); |
341 | if (session->pid < 0) | |
342 | return -1; | |
343 | if (session->pid > 0) { | |
344 | pr_info("reconfig: ruining session [%s:%d]: %s\n", | |
345 | session->name, session->pid, session->run); | |
346 | return 0; | |
347 | } | |
348 | ||
349 | if (chdir(session->base)) { | |
350 | perror("failed: chdir"); | |
351 | return -1; | |
352 | } | |
353 | ||
354 | fd = open("/dev/null", O_RDONLY); | |
355 | if (fd < 0) { | |
356 | perror("failed: open /dev/null"); | |
357 | return -1; | |
358 | } | |
359 | ||
360 | dup2(fd, 0); | |
361 | close(fd); | |
362 | ||
363 | fd = open(SESSION_OUTPUT, O_RDWR|O_CREAT|O_TRUNC, 0644); | |
364 | if (fd < 0) { | |
365 | perror("failed: open session output"); | |
366 | return -1; | |
367 | } | |
368 | ||
369 | dup2(fd, 1); | |
370 | dup2(fd, 2); | |
371 | close(fd); | |
372 | ||
31bf4e7c | 373 | if (mkfifo(SESSION_CONTROL, 0600) && errno != EEXIST) { |
6a6d1804 JO |
374 | perror("failed: create control fifo"); |
375 | return -1; | |
376 | } | |
377 | ||
31bf4e7c | 378 | if (mkfifo(SESSION_ACK, 0600) && errno != EEXIST) { |
6a6d1804 JO |
379 | perror("failed: create ack fifo"); |
380 | return -1; | |
381 | } | |
382 | ||
383 | scnprintf(buf, sizeof(buf), "%s record --control=fifo:%s,%s %s", | |
384 | daemon->perf, SESSION_CONTROL, SESSION_ACK, session->run); | |
c0666261 JO |
385 | |
386 | argv = argv_split(buf, &argc); | |
387 | if (!argv) | |
388 | exit(-1); | |
389 | ||
390 | exit(execve(daemon->perf, argv, NULL)); | |
391 | return -1; | |
392 | } | |
393 | ||
12c1a415 JO |
394 | static pid_t handle_signalfd(struct daemon *daemon) |
395 | { | |
396 | struct daemon_session *session; | |
397 | struct signalfd_siginfo si; | |
398 | ssize_t err; | |
399 | int status; | |
400 | pid_t pid; | |
401 | ||
1833b64f JO |
402 | /* |
403 | * Take signal fd data as pure signal notification and check all | |
404 | * the sessions state. The reason is that multiple signals can get | |
405 | * coalesced in kernel and we can receive only single signal even | |
406 | * if multiple SIGCHLD were generated. | |
407 | */ | |
12c1a415 | 408 | err = read(daemon->signal_fd, &si, sizeof(struct signalfd_siginfo)); |
1833b64f JO |
409 | if (err != sizeof(struct signalfd_siginfo)) { |
410 | pr_err("failed to read signal fd\n"); | |
12c1a415 | 411 | return -1; |
1833b64f | 412 | } |
12c1a415 JO |
413 | |
414 | list_for_each_entry(session, &daemon->sessions, list) { | |
1833b64f JO |
415 | if (session->pid == -1) |
416 | continue; | |
12c1a415 | 417 | |
1833b64f JO |
418 | pid = waitpid(session->pid, &status, WNOHANG); |
419 | if (pid <= 0) | |
12c1a415 JO |
420 | continue; |
421 | ||
1833b64f JO |
422 | if (WIFEXITED(status)) { |
423 | pr_info("session '%s' exited, status=%d\n", | |
424 | session->name, WEXITSTATUS(status)); | |
425 | } else if (WIFSIGNALED(status)) { | |
426 | pr_info("session '%s' killed (signal %d)\n", | |
427 | session->name, WTERMSIG(status)); | |
428 | } else if (WIFSTOPPED(status)) { | |
429 | pr_info("session '%s' stopped (signal %d)\n", | |
430 | session->name, WSTOPSIG(status)); | |
431 | } else { | |
432 | pr_info("session '%s' Unexpected status (0x%x)\n", | |
433 | session->name, status); | |
12c1a415 JO |
434 | } |
435 | ||
436 | session->state = KILL; | |
437 | session->pid = -1; | |
12c1a415 JO |
438 | } |
439 | ||
440 | return 0; | |
441 | } | |
442 | ||
443 | static int daemon_session__wait(struct daemon_session *session, struct daemon *daemon, | |
444 | int secs) | |
445 | { | |
446 | struct pollfd pollfd = { | |
447 | .fd = daemon->signal_fd, | |
448 | .events = POLLIN, | |
449 | }; | |
12c1a415 JO |
450 | time_t start; |
451 | ||
452 | start = time(NULL); | |
453 | ||
454 | do { | |
455 | int err = poll(&pollfd, 1, 1000); | |
456 | ||
457 | if (err > 0) { | |
1833b64f | 458 | handle_signalfd(daemon); |
12c1a415 JO |
459 | } else if (err < 0) { |
460 | perror("failed: poll\n"); | |
461 | return -1; | |
462 | } | |
463 | ||
464 | if (start + secs < time(NULL)) | |
465 | return -1; | |
1833b64f | 466 | } while (session->pid != -1); |
12c1a415 JO |
467 | |
468 | return 0; | |
469 | } | |
470 | ||
471 | static bool daemon__has_alive_session(struct daemon *daemon) | |
472 | { | |
473 | struct daemon_session *session; | |
474 | ||
475 | list_for_each_entry(session, &daemon->sessions, list) { | |
476 | if (session->pid != -1) | |
477 | return true; | |
478 | } | |
479 | ||
480 | return false; | |
481 | } | |
482 | ||
483 | static int daemon__wait(struct daemon *daemon, int secs) | |
484 | { | |
485 | struct pollfd pollfd = { | |
486 | .fd = daemon->signal_fd, | |
487 | .events = POLLIN, | |
488 | }; | |
489 | time_t start; | |
490 | ||
491 | start = time(NULL); | |
492 | ||
493 | do { | |
494 | int err = poll(&pollfd, 1, 1000); | |
495 | ||
496 | if (err > 0) { | |
497 | handle_signalfd(daemon); | |
498 | } else if (err < 0) { | |
499 | perror("failed: poll\n"); | |
500 | return -1; | |
501 | } | |
502 | ||
503 | if (start + secs < time(NULL)) | |
504 | return -1; | |
505 | } while (daemon__has_alive_session(daemon)); | |
506 | ||
507 | return 0; | |
508 | } | |
509 | ||
edcaa479 JO |
510 | static int daemon_session__control(struct daemon_session *session, |
511 | const char *msg, bool do_ack) | |
512 | { | |
513 | struct pollfd pollfd = { .events = POLLIN, }; | |
514 | char control_path[PATH_MAX]; | |
515 | char ack_path[PATH_MAX]; | |
516 | int control, ack = -1, len; | |
517 | char buf[20]; | |
518 | int ret = -1; | |
519 | ssize_t err; | |
520 | ||
521 | /* open the control file */ | |
522 | scnprintf(control_path, sizeof(control_path), "%s/%s", | |
523 | session->base, SESSION_CONTROL); | |
524 | ||
525 | control = open(control_path, O_WRONLY|O_NONBLOCK); | |
526 | if (!control) | |
527 | return -1; | |
528 | ||
529 | if (do_ack) { | |
530 | /* open the ack file */ | |
531 | scnprintf(ack_path, sizeof(ack_path), "%s/%s", | |
532 | session->base, SESSION_ACK); | |
533 | ||
534 | ack = open(ack_path, O_RDONLY, O_NONBLOCK); | |
535 | if (!ack) { | |
536 | close(control); | |
537 | return -1; | |
538 | } | |
539 | } | |
540 | ||
541 | /* write the command */ | |
542 | len = strlen(msg); | |
543 | ||
544 | err = writen(control, msg, len); | |
545 | if (err != len) { | |
546 | pr_err("failed: write to control pipe: %d (%s)\n", | |
547 | errno, control_path); | |
548 | goto out; | |
549 | } | |
550 | ||
551 | if (!do_ack) | |
552 | goto out; | |
553 | ||
554 | /* wait for an ack */ | |
555 | pollfd.fd = ack; | |
556 | ||
557 | if (!poll(&pollfd, 1, 2000)) { | |
558 | pr_err("failed: control ack timeout\n"); | |
559 | goto out; | |
560 | } | |
561 | ||
562 | if (!(pollfd.revents & POLLIN)) { | |
563 | pr_err("failed: did not received an ack\n"); | |
564 | goto out; | |
565 | } | |
566 | ||
567 | err = read(ack, buf, sizeof(buf)); | |
568 | if (err > 0) | |
569 | ret = strcmp(buf, "ack\n"); | |
570 | else | |
571 | perror("failed: read ack %d\n"); | |
572 | ||
573 | out: | |
574 | if (ack != -1) | |
575 | close(ack); | |
576 | ||
577 | close(control); | |
578 | return ret; | |
579 | } | |
580 | ||
ed36b704 JO |
581 | static int setup_server_socket(struct daemon *daemon) |
582 | { | |
583 | struct sockaddr_un addr; | |
584 | char path[PATH_MAX]; | |
585 | int fd = socket(AF_UNIX, SOCK_STREAM, 0); | |
586 | ||
587 | if (fd < 0) { | |
588 | fprintf(stderr, "socket: %s\n", strerror(errno)); | |
589 | return -1; | |
590 | } | |
591 | ||
592 | if (fcntl(fd, F_SETFD, FD_CLOEXEC)) { | |
593 | perror("failed: fcntl FD_CLOEXEC"); | |
594 | close(fd); | |
595 | return -1; | |
596 | } | |
597 | ||
598 | scnprintf(path, sizeof(path), "%s/control", daemon->base); | |
599 | ||
600 | if (strlen(path) + 1 >= sizeof(addr.sun_path)) { | |
601 | pr_err("failed: control path too long '%s'\n", path); | |
602 | close(fd); | |
603 | return -1; | |
604 | } | |
605 | ||
606 | memset(&addr, 0, sizeof(addr)); | |
607 | addr.sun_family = AF_UNIX; | |
608 | ||
609 | strlcpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); | |
610 | unlink(path); | |
611 | ||
612 | if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { | |
613 | perror("failed: bind"); | |
614 | close(fd); | |
615 | return -1; | |
616 | } | |
617 | ||
618 | if (listen(fd, 1) == -1) { | |
619 | perror("failed: listen"); | |
620 | close(fd); | |
621 | return -1; | |
622 | } | |
623 | ||
624 | return fd; | |
625 | } | |
626 | ||
b325f7be JO |
627 | enum { |
628 | CMD_LIST = 0, | |
2d6914cd | 629 | CMD_SIGNAL = 1, |
23c5831e | 630 | CMD_STOP = 2, |
edcaa479 | 631 | CMD_PING = 3, |
b325f7be JO |
632 | CMD_MAX, |
633 | }; | |
634 | ||
2d6914cd JO |
635 | #define SESSION_MAX 64 |
636 | ||
ed36b704 JO |
637 | union cmd { |
638 | int cmd; | |
b325f7be JO |
639 | |
640 | /* CMD_LIST */ | |
641 | struct { | |
642 | int cmd; | |
643 | int verbose; | |
644 | char csv_sep; | |
645 | } list; | |
2d6914cd JO |
646 | |
647 | /* CMD_SIGNAL */ | |
648 | struct { | |
649 | int cmd; | |
650 | int sig; | |
651 | char name[SESSION_MAX]; | |
652 | } signal; | |
edcaa479 JO |
653 | |
654 | /* CMD_PING */ | |
655 | struct { | |
656 | int cmd; | |
657 | char name[SESSION_MAX]; | |
658 | } ping; | |
659 | }; | |
660 | ||
661 | enum { | |
662 | PING_OK = 0, | |
663 | PING_FAIL = 1, | |
664 | PING_MAX, | |
ed36b704 JO |
665 | }; |
666 | ||
edcaa479 JO |
667 | static int daemon_session__ping(struct daemon_session *session) |
668 | { | |
669 | return daemon_session__control(session, "ping", true) ? PING_FAIL : PING_OK; | |
670 | } | |
671 | ||
b325f7be JO |
672 | static int cmd_session_list(struct daemon *daemon, union cmd *cmd, FILE *out) |
673 | { | |
674 | char csv_sep = cmd->list.csv_sep; | |
675 | struct daemon_session *session; | |
5bdee4f0 | 676 | time_t curr = time(NULL); |
b325f7be JO |
677 | |
678 | if (csv_sep) { | |
679 | fprintf(out, "%d%c%s%c%s%c%s/%s", | |
680 | /* pid daemon */ | |
681 | getpid(), csv_sep, "daemon", | |
682 | /* base */ | |
683 | csv_sep, daemon->base, | |
684 | /* output */ | |
685 | csv_sep, daemon->base, SESSION_OUTPUT); | |
686 | ||
8c98be6c JO |
687 | fprintf(out, "%c%s/%s", |
688 | /* lock */ | |
689 | csv_sep, daemon->base, "lock"); | |
690 | ||
5bdee4f0 JO |
691 | fprintf(out, "%c%lu", |
692 | /* session up time */ | |
693 | csv_sep, (curr - daemon->start) / 60); | |
694 | ||
b325f7be JO |
695 | fprintf(out, "\n"); |
696 | } else { | |
697 | fprintf(out, "[%d:daemon] base: %s\n", getpid(), daemon->base); | |
698 | if (cmd->list.verbose) { | |
699 | fprintf(out, " output: %s/%s\n", | |
700 | daemon->base, SESSION_OUTPUT); | |
8c98be6c JO |
701 | fprintf(out, " lock: %s/lock\n", |
702 | daemon->base); | |
5bdee4f0 JO |
703 | fprintf(out, " up: %lu minutes\n", |
704 | (curr - daemon->start) / 60); | |
b325f7be JO |
705 | } |
706 | } | |
707 | ||
708 | list_for_each_entry(session, &daemon->sessions, list) { | |
709 | if (csv_sep) { | |
710 | fprintf(out, "%d%c%s%c%s", | |
711 | /* pid */ | |
712 | session->pid, | |
713 | /* name */ | |
714 | csv_sep, session->name, | |
715 | /* base */ | |
716 | csv_sep, session->run); | |
717 | ||
718 | fprintf(out, "%c%s%c%s/%s", | |
719 | /* session dir */ | |
720 | csv_sep, session->base, | |
721 | /* session output */ | |
722 | csv_sep, session->base, SESSION_OUTPUT); | |
723 | ||
6a6d1804 JO |
724 | fprintf(out, "%c%s/%s%c%s/%s", |
725 | /* session control */ | |
726 | csv_sep, session->base, SESSION_CONTROL, | |
727 | /* session ack */ | |
728 | csv_sep, session->base, SESSION_ACK); | |
729 | ||
5bdee4f0 JO |
730 | fprintf(out, "%c%lu", |
731 | /* session up time */ | |
732 | csv_sep, (curr - session->start) / 60); | |
733 | ||
b325f7be JO |
734 | fprintf(out, "\n"); |
735 | } else { | |
736 | fprintf(out, "[%d:%s] perf record %s\n", | |
737 | session->pid, session->name, session->run); | |
738 | if (!cmd->list.verbose) | |
739 | continue; | |
740 | fprintf(out, " base: %s\n", | |
741 | session->base); | |
742 | fprintf(out, " output: %s/%s\n", | |
743 | session->base, SESSION_OUTPUT); | |
6a6d1804 JO |
744 | fprintf(out, " control: %s/%s\n", |
745 | session->base, SESSION_CONTROL); | |
746 | fprintf(out, " ack: %s/%s\n", | |
747 | session->base, SESSION_ACK); | |
5bdee4f0 JO |
748 | fprintf(out, " up: %lu minutes\n", |
749 | (curr - session->start) / 60); | |
b325f7be JO |
750 | } |
751 | } | |
752 | ||
753 | return 0; | |
754 | } | |
755 | ||
2d6914cd JO |
756 | static int daemon_session__signal(struct daemon_session *session, int sig) |
757 | { | |
758 | if (session->pid < 0) | |
759 | return -1; | |
760 | return kill(session->pid, sig); | |
761 | } | |
762 | ||
763 | static int cmd_session_kill(struct daemon *daemon, union cmd *cmd, FILE *out) | |
764 | { | |
765 | struct daemon_session *session; | |
766 | bool all = false; | |
767 | ||
768 | all = !strcmp(cmd->signal.name, "all"); | |
769 | ||
770 | list_for_each_entry(session, &daemon->sessions, list) { | |
771 | if (all || !strcmp(cmd->signal.name, session->name)) { | |
772 | daemon_session__signal(session, cmd->signal.sig); | |
773 | fprintf(out, "signal %d sent to session '%s [%d]'\n", | |
774 | cmd->signal.sig, session->name, session->pid); | |
775 | } | |
776 | } | |
777 | ||
778 | return 0; | |
779 | } | |
780 | ||
edcaa479 JO |
781 | static const char *ping_str[PING_MAX] = { |
782 | [PING_OK] = "OK", | |
783 | [PING_FAIL] = "FAIL", | |
784 | }; | |
785 | ||
786 | static int cmd_session_ping(struct daemon *daemon, union cmd *cmd, FILE *out) | |
787 | { | |
788 | struct daemon_session *session; | |
789 | bool all = false, found = false; | |
790 | ||
791 | all = !strcmp(cmd->ping.name, "all"); | |
792 | ||
793 | list_for_each_entry(session, &daemon->sessions, list) { | |
794 | if (all || !strcmp(cmd->ping.name, session->name)) { | |
795 | int state = daemon_session__ping(session); | |
796 | ||
797 | fprintf(out, "%-4s %s\n", ping_str[state], session->name); | |
798 | found = true; | |
799 | } | |
800 | } | |
801 | ||
802 | if (!found && !all) { | |
803 | fprintf(out, "%-4s %s (not found)\n", | |
804 | ping_str[PING_FAIL], cmd->ping.name); | |
805 | } | |
806 | return 0; | |
807 | } | |
808 | ||
b325f7be | 809 | static int handle_server_socket(struct daemon *daemon, int sock_fd) |
ed36b704 JO |
810 | { |
811 | int ret = -1, fd; | |
812 | FILE *out = NULL; | |
813 | union cmd cmd; | |
814 | ||
815 | fd = accept(sock_fd, NULL, NULL); | |
816 | if (fd < 0) { | |
817 | perror("failed: accept"); | |
818 | return -1; | |
819 | } | |
820 | ||
821 | if (sizeof(cmd) != readn(fd, &cmd, sizeof(cmd))) { | |
822 | perror("failed: read"); | |
823 | goto out; | |
824 | } | |
825 | ||
826 | out = fdopen(fd, "w"); | |
827 | if (!out) { | |
828 | perror("failed: fdopen"); | |
829 | goto out; | |
830 | } | |
831 | ||
832 | switch (cmd.cmd) { | |
b325f7be JO |
833 | case CMD_LIST: |
834 | ret = cmd_session_list(daemon, &cmd, out); | |
835 | break; | |
2d6914cd JO |
836 | case CMD_SIGNAL: |
837 | ret = cmd_session_kill(daemon, &cmd, out); | |
838 | break; | |
23c5831e JO |
839 | case CMD_STOP: |
840 | done = 1; | |
841 | ret = 0; | |
842 | pr_debug("perf daemon is exciting\n"); | |
843 | break; | |
edcaa479 JO |
844 | case CMD_PING: |
845 | ret = cmd_session_ping(daemon, &cmd, out); | |
846 | break; | |
ed36b704 JO |
847 | default: |
848 | break; | |
849 | } | |
850 | ||
851 | fclose(out); | |
852 | out: | |
853 | /* If out is defined, then fd is closed via fclose. */ | |
854 | if (!out) | |
855 | close(fd); | |
856 | return ret; | |
857 | } | |
858 | ||
90b0aad8 JO |
859 | static int setup_client_socket(struct daemon *daemon) |
860 | { | |
861 | struct sockaddr_un addr; | |
862 | char path[PATH_MAX]; | |
863 | int fd = socket(AF_UNIX, SOCK_STREAM, 0); | |
864 | ||
865 | if (fd == -1) { | |
866 | perror("failed: socket"); | |
867 | return -1; | |
868 | } | |
869 | ||
870 | scnprintf(path, sizeof(path), "%s/control", daemon->base); | |
871 | ||
872 | if (strlen(path) + 1 >= sizeof(addr.sun_path)) { | |
873 | pr_err("failed: control path too long '%s'\n", path); | |
874 | close(fd); | |
875 | return -1; | |
876 | } | |
877 | ||
878 | memset(&addr, 0, sizeof(addr)); | |
879 | addr.sun_family = AF_UNIX; | |
880 | strlcpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); | |
881 | ||
882 | if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) { | |
883 | perror("failed: connect"); | |
884 | close(fd); | |
885 | return -1; | |
886 | } | |
887 | ||
888 | return fd; | |
889 | } | |
890 | ||
12c1a415 JO |
891 | static void daemon_session__kill(struct daemon_session *session, |
892 | struct daemon *daemon) | |
c0666261 | 893 | { |
6d6162d5 JO |
894 | int how = 0; |
895 | ||
896 | do { | |
897 | switch (how) { | |
898 | case 0: | |
899 | daemon_session__control(session, "stop", false); | |
900 | break; | |
901 | case 1: | |
902 | daemon_session__signal(session, SIGTERM); | |
903 | break; | |
904 | case 2: | |
905 | daemon_session__signal(session, SIGKILL); | |
906 | break; | |
907 | default: | |
9f177fd8 JO |
908 | pr_err("failed to wait for session %s\n", |
909 | session->name); | |
910 | return; | |
6d6162d5 JO |
911 | } |
912 | how++; | |
913 | ||
914 | } while (daemon_session__wait(session, daemon, 10)); | |
c0666261 JO |
915 | } |
916 | ||
917 | static void daemon__signal(struct daemon *daemon, int sig) | |
918 | { | |
919 | struct daemon_session *session; | |
920 | ||
921 | list_for_each_entry(session, &daemon->sessions, list) | |
922 | daemon_session__signal(session, sig); | |
923 | } | |
924 | ||
925 | static void daemon_session__delete(struct daemon_session *session) | |
926 | { | |
927 | free(session->base); | |
928 | free(session->name); | |
929 | free(session->run); | |
930 | free(session); | |
931 | } | |
932 | ||
933 | static void daemon_session__remove(struct daemon_session *session) | |
934 | { | |
935 | list_del(&session->list); | |
936 | daemon_session__delete(session); | |
937 | } | |
938 | ||
6d6162d5 JO |
939 | static void daemon__stop(struct daemon *daemon) |
940 | { | |
941 | struct daemon_session *session; | |
942 | ||
943 | list_for_each_entry(session, &daemon->sessions, list) | |
944 | daemon_session__control(session, "stop", false); | |
945 | } | |
946 | ||
c0666261 JO |
947 | static void daemon__kill(struct daemon *daemon) |
948 | { | |
6d6162d5 JO |
949 | int how = 0; |
950 | ||
951 | do { | |
952 | switch (how) { | |
953 | case 0: | |
954 | daemon__stop(daemon); | |
955 | break; | |
956 | case 1: | |
957 | daemon__signal(daemon, SIGTERM); | |
958 | break; | |
959 | case 2: | |
960 | daemon__signal(daemon, SIGKILL); | |
961 | break; | |
962 | default: | |
9f177fd8 JO |
963 | pr_err("failed to wait for sessions\n"); |
964 | return; | |
6d6162d5 JO |
965 | } |
966 | how++; | |
967 | ||
968 | } while (daemon__wait(daemon, 10)); | |
c0666261 JO |
969 | } |
970 | ||
fc1dcb1e JO |
971 | static void daemon__exit(struct daemon *daemon) |
972 | { | |
c0666261 JO |
973 | struct daemon_session *session, *h; |
974 | ||
975 | list_for_each_entry_safe(session, h, &daemon->sessions, list) | |
976 | daemon_session__remove(session); | |
977 | ||
fc1dcb1e | 978 | free(daemon->config_real); |
3cda0625 | 979 | free(daemon->config_base); |
5631d100 | 980 | free(daemon->base); |
fc1dcb1e JO |
981 | } |
982 | ||
c0666261 JO |
983 | static int daemon__reconfig(struct daemon *daemon) |
984 | { | |
985 | struct daemon_session *session, *n; | |
986 | ||
987 | list_for_each_entry_safe(session, n, &daemon->sessions, list) { | |
988 | /* No change. */ | |
989 | if (session->state == OK) | |
990 | continue; | |
991 | ||
992 | /* Remove session. */ | |
993 | if (session->state == KILL) { | |
994 | if (session->pid > 0) { | |
12c1a415 | 995 | daemon_session__kill(session, daemon); |
c0666261 JO |
996 | pr_info("reconfig: session '%s' killed\n", session->name); |
997 | } | |
998 | daemon_session__remove(session); | |
999 | continue; | |
1000 | } | |
1001 | ||
1002 | /* Reconfig session. */ | |
1003 | if (session->pid > 0) { | |
12c1a415 | 1004 | daemon_session__kill(session, daemon); |
c0666261 JO |
1005 | pr_info("reconfig: session '%s' killed\n", session->name); |
1006 | } | |
1007 | if (daemon_session__run(session, daemon)) | |
1008 | return -1; | |
1009 | ||
1010 | session->state = OK; | |
1011 | } | |
1012 | ||
1013 | return 0; | |
1014 | } | |
1015 | ||
3cda0625 JO |
1016 | static int setup_config_changes(struct daemon *daemon) |
1017 | { | |
1018 | char *basen = strdup(daemon->config_real); | |
1019 | char *dirn = strdup(daemon->config_real); | |
1020 | char *base, *dir; | |
1021 | int fd, wd = -1; | |
1022 | ||
1023 | if (!dirn || !basen) | |
1024 | goto out; | |
1025 | ||
1026 | fd = inotify_init1(IN_NONBLOCK|O_CLOEXEC); | |
1027 | if (fd < 0) { | |
1028 | perror("failed: inotify_init"); | |
1029 | goto out; | |
1030 | } | |
1031 | ||
1032 | dir = dirname(dirn); | |
1033 | base = basename(basen); | |
1034 | pr_debug("config file: %s, dir: %s\n", base, dir); | |
1035 | ||
1036 | wd = inotify_add_watch(fd, dir, IN_CLOSE_WRITE); | |
1037 | if (wd >= 0) { | |
1038 | daemon->config_base = strdup(base); | |
1039 | if (!daemon->config_base) { | |
1040 | close(fd); | |
1041 | wd = -1; | |
1042 | } | |
1043 | } else { | |
1044 | perror("failed: inotify_add_watch"); | |
1045 | } | |
1046 | ||
1047 | out: | |
1048 | free(basen); | |
1049 | free(dirn); | |
1050 | return wd < 0 ? -1 : fd; | |
1051 | } | |
1052 | ||
1053 | static bool process_inotify_event(struct daemon *daemon, char *buf, ssize_t len) | |
1054 | { | |
1055 | char *p = buf; | |
1056 | ||
1057 | while (p < (buf + len)) { | |
1058 | struct inotify_event *event = (struct inotify_event *) p; | |
1059 | ||
1060 | /* | |
1061 | * We monitor config directory, check if our | |
1062 | * config file was changes. | |
1063 | */ | |
1064 | if ((event->mask & IN_CLOSE_WRITE) && | |
1065 | !(event->mask & IN_ISDIR)) { | |
1066 | if (!strcmp(event->name, daemon->config_base)) | |
1067 | return true; | |
1068 | } | |
1069 | p += sizeof(*event) + event->len; | |
1070 | } | |
1071 | return false; | |
1072 | } | |
1073 | ||
1074 | static int handle_config_changes(struct daemon *daemon, int conf_fd, | |
1075 | bool *config_changed) | |
1076 | { | |
1077 | char buf[4096]; | |
1078 | ssize_t len; | |
1079 | ||
1080 | while (!(*config_changed)) { | |
1081 | len = read(conf_fd, buf, sizeof(buf)); | |
1082 | if (len == -1) { | |
1083 | if (errno != EAGAIN) { | |
1084 | perror("failed: read"); | |
1085 | return -1; | |
1086 | } | |
1087 | return 0; | |
1088 | } | |
1089 | *config_changed = process_inotify_event(daemon, buf, len); | |
1090 | } | |
1091 | return 0; | |
1092 | } | |
1093 | ||
fc1dcb1e JO |
1094 | static int setup_config(struct daemon *daemon) |
1095 | { | |
5631d100 JO |
1096 | if (daemon->base_user) { |
1097 | daemon->base = strdup(daemon->base_user); | |
1098 | if (!daemon->base) | |
1099 | return -ENOMEM; | |
1100 | } | |
1101 | ||
fc1dcb1e JO |
1102 | if (daemon->config) { |
1103 | char *real = realpath(daemon->config, NULL); | |
1104 | ||
1105 | if (!real) { | |
1106 | perror("failed: realpath"); | |
1107 | return -1; | |
1108 | } | |
1109 | daemon->config_real = real; | |
1110 | return 0; | |
1111 | } | |
1112 | ||
1113 | if (perf_config_system() && !access(perf_etc_perfconfig(), R_OK)) | |
1114 | daemon->config_real = strdup(perf_etc_perfconfig()); | |
1115 | else if (perf_config_global() && perf_home_perfconfig()) | |
1116 | daemon->config_real = strdup(perf_home_perfconfig()); | |
1117 | ||
1118 | return daemon->config_real ? 0 : -1; | |
1119 | } | |
1120 | ||
8c98be6c JO |
1121 | #ifndef F_TLOCK |
1122 | #define F_TLOCK 2 | |
1123 | ||
1124 | #include <sys/file.h> | |
1125 | ||
1126 | static int lockf(int fd, int cmd, off_t len) | |
1127 | { | |
1128 | if (cmd != F_TLOCK || len != 0) | |
1129 | return -1; | |
1130 | ||
1131 | return flock(fd, LOCK_EX | LOCK_NB); | |
1132 | } | |
1133 | #endif // F_TLOCK | |
1134 | ||
1135 | /* | |
1136 | * Each daemon tries to create and lock BASE/lock file, | |
1137 | * if it's successful we are sure we're the only daemon | |
1138 | * running over the BASE. | |
1139 | * | |
1140 | * Once daemon is finished, file descriptor to lock file | |
1141 | * is closed and lock is released. | |
1142 | */ | |
1143 | static int check_lock(struct daemon *daemon) | |
1144 | { | |
1145 | char path[PATH_MAX]; | |
1146 | char buf[20]; | |
1147 | int fd, pid; | |
1148 | ssize_t len; | |
1149 | ||
1150 | scnprintf(path, sizeof(path), "%s/lock", daemon->base); | |
1151 | ||
1152 | fd = open(path, O_RDWR|O_CREAT|O_CLOEXEC, 0640); | |
1153 | if (fd < 0) | |
1154 | return -1; | |
1155 | ||
1156 | if (lockf(fd, F_TLOCK, 0) < 0) { | |
1157 | filename__read_int(path, &pid); | |
1158 | fprintf(stderr, "failed: another perf daemon (pid %d) owns %s\n", | |
1159 | pid, daemon->base); | |
1160 | close(fd); | |
1161 | return -1; | |
1162 | } | |
1163 | ||
1164 | scnprintf(buf, sizeof(buf), "%d", getpid()); | |
1165 | len = strlen(buf); | |
1166 | ||
1167 | if (write(fd, buf, len) != len) { | |
1168 | perror("failed: write"); | |
1169 | close(fd); | |
1170 | return -1; | |
1171 | } | |
1172 | ||
1173 | if (ftruncate(fd, len)) { | |
1174 | perror("failed: ftruncate"); | |
1175 | close(fd); | |
1176 | return -1; | |
1177 | } | |
1178 | ||
1179 | return 0; | |
1180 | } | |
1181 | ||
88adb119 JO |
1182 | static int go_background(struct daemon *daemon) |
1183 | { | |
1184 | int pid, fd; | |
1185 | ||
1186 | pid = fork(); | |
1187 | if (pid < 0) | |
1188 | return -1; | |
1189 | ||
1190 | if (pid > 0) | |
1191 | return 1; | |
1192 | ||
1193 | if (setsid() < 0) | |
1194 | return -1; | |
1195 | ||
8c98be6c JO |
1196 | if (check_lock(daemon)) |
1197 | return -1; | |
1198 | ||
88adb119 JO |
1199 | umask(0); |
1200 | ||
1201 | if (chdir(daemon->base)) { | |
1202 | perror("failed: chdir"); | |
1203 | return -1; | |
1204 | } | |
1205 | ||
1206 | fd = open("output", O_RDWR|O_CREAT|O_TRUNC, 0644); | |
1207 | if (fd < 0) { | |
1208 | perror("failed: open"); | |
1209 | return -1; | |
1210 | } | |
1211 | ||
1212 | if (fcntl(fd, F_SETFD, FD_CLOEXEC)) { | |
1213 | perror("failed: fcntl FD_CLOEXEC"); | |
1214 | close(fd); | |
1215 | return -1; | |
1216 | } | |
1217 | ||
1218 | close(0); | |
1219 | dup2(fd, 1); | |
1220 | dup2(fd, 2); | |
1221 | close(fd); | |
1222 | ||
1223 | daemon->out = fdopen(1, "w"); | |
1224 | if (!daemon->out) { | |
1225 | close(1); | |
1226 | close(2); | |
1227 | return -1; | |
1228 | } | |
1229 | ||
1230 | setbuf(daemon->out, NULL); | |
1231 | return 0; | |
1232 | } | |
1233 | ||
12c1a415 JO |
1234 | static int setup_signalfd(struct daemon *daemon) |
1235 | { | |
1236 | sigset_t mask; | |
1237 | ||
1238 | sigemptyset(&mask); | |
1239 | sigaddset(&mask, SIGCHLD); | |
1240 | ||
1241 | if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) | |
1242 | return -1; | |
1243 | ||
1244 | daemon->signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); | |
1245 | return daemon->signal_fd; | |
1246 | } | |
1247 | ||
d450bc50 JO |
1248 | static int __cmd_start(struct daemon *daemon, struct option parent_options[], |
1249 | int argc, const char **argv) | |
1250 | { | |
88adb119 | 1251 | bool foreground = false; |
d450bc50 | 1252 | struct option start_options[] = { |
88adb119 | 1253 | OPT_BOOLEAN('f', "foreground", &foreground, "stay on console"), |
d450bc50 JO |
1254 | OPT_PARENT(parent_options), |
1255 | OPT_END() | |
1256 | }; | |
12c1a415 JO |
1257 | int sock_fd = -1, conf_fd = -1, signal_fd = -1; |
1258 | int sock_pos, file_pos, signal_pos; | |
ed36b704 | 1259 | struct fdarray fda; |
d450bc50 JO |
1260 | int err = 0; |
1261 | ||
1262 | argc = parse_options(argc, argv, start_options, daemon_usage, 0); | |
1263 | if (argc) | |
1264 | usage_with_options(daemon_usage, start_options); | |
1265 | ||
5bdee4f0 JO |
1266 | daemon->start = time(NULL); |
1267 | ||
fc1dcb1e JO |
1268 | if (setup_config(daemon)) { |
1269 | pr_err("failed: config not found\n"); | |
1270 | return -1; | |
1271 | } | |
1272 | ||
c0666261 JO |
1273 | if (setup_server_config(daemon)) |
1274 | return -1; | |
1275 | ||
8c98be6c JO |
1276 | if (foreground && check_lock(daemon)) |
1277 | return -1; | |
1278 | ||
88adb119 JO |
1279 | if (!foreground) { |
1280 | err = go_background(daemon); | |
1281 | if (err) { | |
1282 | /* original process, exit normally */ | |
1283 | if (err == 1) | |
1284 | err = 0; | |
1285 | daemon__exit(daemon); | |
1286 | return err; | |
1287 | } | |
1288 | } | |
1289 | ||
d450bc50 JO |
1290 | debug_set_file(daemon->out); |
1291 | debug_set_display_time(true); | |
1292 | ||
1293 | pr_info("daemon started (pid %d)\n", getpid()); | |
1294 | ||
12c1a415 | 1295 | fdarray__init(&fda, 3); |
ed36b704 JO |
1296 | |
1297 | sock_fd = setup_server_socket(daemon); | |
1298 | if (sock_fd < 0) | |
1299 | goto out; | |
1300 | ||
3cda0625 JO |
1301 | conf_fd = setup_config_changes(daemon); |
1302 | if (conf_fd < 0) | |
1303 | goto out; | |
1304 | ||
12c1a415 JO |
1305 | signal_fd = setup_signalfd(daemon); |
1306 | if (signal_fd < 0) | |
1307 | goto out; | |
1308 | ||
ed36b704 JO |
1309 | sock_pos = fdarray__add(&fda, sock_fd, POLLIN|POLLERR|POLLHUP, 0); |
1310 | if (sock_pos < 0) | |
1311 | goto out; | |
1312 | ||
3cda0625 JO |
1313 | file_pos = fdarray__add(&fda, conf_fd, POLLIN|POLLERR|POLLHUP, 0); |
1314 | if (file_pos < 0) | |
1315 | goto out; | |
1316 | ||
12c1a415 JO |
1317 | signal_pos = fdarray__add(&fda, signal_fd, POLLIN|POLLERR|POLLHUP, 0); |
1318 | if (signal_pos < 0) | |
1319 | goto out; | |
1320 | ||
d450bc50 JO |
1321 | signal(SIGINT, sig_handler); |
1322 | signal(SIGTERM, sig_handler); | |
edcaa479 | 1323 | signal(SIGPIPE, SIG_IGN); |
d450bc50 JO |
1324 | |
1325 | while (!done && !err) { | |
c0666261 JO |
1326 | err = daemon__reconfig(daemon); |
1327 | ||
1328 | if (!err && fdarray__poll(&fda, -1)) { | |
1329 | bool reconfig = false; | |
1330 | ||
ed36b704 JO |
1331 | if (fda.entries[sock_pos].revents & POLLIN) |
1332 | err = handle_server_socket(daemon, sock_fd); | |
3cda0625 JO |
1333 | if (fda.entries[file_pos].revents & POLLIN) |
1334 | err = handle_config_changes(daemon, conf_fd, &reconfig); | |
12c1a415 JO |
1335 | if (fda.entries[signal_pos].revents & POLLIN) |
1336 | err = handle_signalfd(daemon) < 0; | |
c0666261 JO |
1337 | |
1338 | if (reconfig) | |
1339 | err = setup_server_config(daemon); | |
ed36b704 | 1340 | } |
d450bc50 JO |
1341 | } |
1342 | ||
ed36b704 JO |
1343 | out: |
1344 | fdarray__exit(&fda); | |
1345 | ||
c0666261 | 1346 | daemon__kill(daemon); |
fc1dcb1e JO |
1347 | daemon__exit(daemon); |
1348 | ||
ed36b704 JO |
1349 | if (sock_fd != -1) |
1350 | close(sock_fd); | |
3cda0625 JO |
1351 | if (conf_fd != -1) |
1352 | close(conf_fd); | |
1833b64f | 1353 | if (signal_fd != -1) |
12c1a415 | 1354 | close(signal_fd); |
ed36b704 | 1355 | |
d450bc50 JO |
1356 | pr_info("daemon exited\n"); |
1357 | fclose(daemon->out); | |
1358 | return err; | |
1359 | } | |
1360 | ||
90b0aad8 JO |
1361 | static int send_cmd(struct daemon *daemon, union cmd *cmd) |
1362 | { | |
1363 | int ret = -1, fd; | |
1364 | char *line = NULL; | |
1365 | size_t len = 0; | |
1366 | ssize_t nread; | |
1367 | FILE *in = NULL; | |
1368 | ||
1369 | if (setup_client_config(daemon)) | |
1370 | return -1; | |
1371 | ||
1372 | fd = setup_client_socket(daemon); | |
1373 | if (fd < 0) | |
1374 | return -1; | |
1375 | ||
1376 | if (sizeof(*cmd) != writen(fd, cmd, sizeof(*cmd))) { | |
1377 | perror("failed: write"); | |
1378 | goto out; | |
1379 | } | |
1380 | ||
1381 | in = fdopen(fd, "r"); | |
1382 | if (!in) { | |
1383 | perror("failed: fdopen"); | |
1384 | goto out; | |
1385 | } | |
1386 | ||
1387 | while ((nread = getline(&line, &len, in)) != -1) { | |
1388 | if (fwrite(line, nread, 1, stdout) != 1) | |
1389 | goto out_fclose; | |
1390 | fflush(stdout); | |
1391 | } | |
1392 | ||
1393 | ret = 0; | |
1394 | out_fclose: | |
1395 | fclose(in); | |
1396 | free(line); | |
1397 | out: | |
1398 | /* If in is defined, then fd is closed via fclose. */ | |
1399 | if (!in) | |
1400 | close(fd); | |
1401 | return ret; | |
1402 | } | |
1403 | ||
b325f7be JO |
1404 | static int send_cmd_list(struct daemon *daemon) |
1405 | { | |
1406 | union cmd cmd = { .cmd = CMD_LIST, }; | |
1407 | ||
1408 | cmd.list.verbose = verbose; | |
1409 | cmd.list.csv_sep = daemon->csv_sep ? *daemon->csv_sep : 0; | |
1410 | ||
1411 | return send_cmd(daemon, &cmd); | |
1412 | } | |
1413 | ||
2d6914cd JO |
1414 | static int __cmd_signal(struct daemon *daemon, struct option parent_options[], |
1415 | int argc, const char **argv) | |
1416 | { | |
1417 | const char *name = "all"; | |
1418 | struct option start_options[] = { | |
1419 | OPT_STRING(0, "session", &name, "session", | |
1420 | "Sent signal to specific session"), | |
1421 | OPT_PARENT(parent_options), | |
1422 | OPT_END() | |
1423 | }; | |
1424 | union cmd cmd; | |
1425 | ||
1426 | argc = parse_options(argc, argv, start_options, daemon_usage, 0); | |
1427 | if (argc) | |
1428 | usage_with_options(daemon_usage, start_options); | |
1429 | ||
1430 | if (setup_config(daemon)) { | |
1431 | pr_err("failed: config not found\n"); | |
1432 | return -1; | |
1433 | } | |
1434 | ||
1435 | cmd.signal.cmd = CMD_SIGNAL, | |
1436 | cmd.signal.sig = SIGUSR2; | |
1437 | strncpy(cmd.signal.name, name, sizeof(cmd.signal.name) - 1); | |
1438 | ||
1439 | return send_cmd(daemon, &cmd); | |
1440 | } | |
1441 | ||
23c5831e JO |
1442 | static int __cmd_stop(struct daemon *daemon, struct option parent_options[], |
1443 | int argc, const char **argv) | |
1444 | { | |
1445 | struct option start_options[] = { | |
1446 | OPT_PARENT(parent_options), | |
1447 | OPT_END() | |
1448 | }; | |
1449 | union cmd cmd = { .cmd = CMD_STOP, }; | |
1450 | ||
1451 | argc = parse_options(argc, argv, start_options, daemon_usage, 0); | |
1452 | if (argc) | |
1453 | usage_with_options(daemon_usage, start_options); | |
1454 | ||
1455 | if (setup_config(daemon)) { | |
1456 | pr_err("failed: config not found\n"); | |
1457 | return -1; | |
1458 | } | |
1459 | ||
1460 | return send_cmd(daemon, &cmd); | |
1461 | } | |
1462 | ||
edcaa479 JO |
1463 | static int __cmd_ping(struct daemon *daemon, struct option parent_options[], |
1464 | int argc, const char **argv) | |
1465 | { | |
1466 | const char *name = "all"; | |
1467 | struct option ping_options[] = { | |
1468 | OPT_STRING(0, "session", &name, "session", | |
1469 | "Ping to specific session"), | |
1470 | OPT_PARENT(parent_options), | |
1471 | OPT_END() | |
1472 | }; | |
1473 | union cmd cmd = { .cmd = CMD_PING, }; | |
1474 | ||
1475 | argc = parse_options(argc, argv, ping_options, daemon_usage, 0); | |
1476 | if (argc) | |
1477 | usage_with_options(daemon_usage, ping_options); | |
1478 | ||
1479 | if (setup_config(daemon)) { | |
1480 | pr_err("failed: config not found\n"); | |
1481 | return -1; | |
1482 | } | |
1483 | ||
1484 | scnprintf(cmd.ping.name, sizeof(cmd.ping.name), "%s", name); | |
1485 | return send_cmd(daemon, &cmd); | |
1486 | } | |
1487 | ||
d450bc50 JO |
1488 | int cmd_daemon(int argc, const char **argv) |
1489 | { | |
1490 | struct option daemon_options[] = { | |
1491 | OPT_INCR('v', "verbose", &verbose, "be more verbose"), | |
fc1dcb1e JO |
1492 | OPT_STRING(0, "config", &__daemon.config, |
1493 | "config file", "config file path"), | |
5631d100 JO |
1494 | OPT_STRING(0, "base", &__daemon.base_user, |
1495 | "directory", "base directory"), | |
b325f7be JO |
1496 | OPT_STRING_OPTARG('x', "field-separator", &__daemon.csv_sep, |
1497 | "field separator", "print counts with custom separator", ","), | |
d450bc50 JO |
1498 | OPT_END() |
1499 | }; | |
1500 | ||
1501 | perf_exe(__daemon.perf, sizeof(__daemon.perf)); | |
1502 | __daemon.out = stdout; | |
1503 | ||
1504 | argc = parse_options(argc, argv, daemon_options, daemon_usage, | |
1505 | PARSE_OPT_STOP_AT_NON_OPTION); | |
1506 | ||
1507 | if (argc) { | |
1508 | if (!strcmp(argv[0], "start")) | |
1509 | return __cmd_start(&__daemon, daemon_options, argc, argv); | |
2d6914cd JO |
1510 | if (!strcmp(argv[0], "signal")) |
1511 | return __cmd_signal(&__daemon, daemon_options, argc, argv); | |
23c5831e JO |
1512 | else if (!strcmp(argv[0], "stop")) |
1513 | return __cmd_stop(&__daemon, daemon_options, argc, argv); | |
edcaa479 JO |
1514 | else if (!strcmp(argv[0], "ping")) |
1515 | return __cmd_ping(&__daemon, daemon_options, argc, argv); | |
d450bc50 JO |
1516 | |
1517 | pr_err("failed: unknown command '%s'\n", argv[0]); | |
1518 | return -1; | |
1519 | } | |
1520 | ||
fc1dcb1e JO |
1521 | if (setup_config(&__daemon)) { |
1522 | pr_err("failed: config not found\n"); | |
1523 | return -1; | |
1524 | } | |
1525 | ||
b325f7be | 1526 | return send_cmd_list(&__daemon); |
d450bc50 | 1527 | } |