1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <internal/lib.h> |
3 | #include <subcmd/parse-options.h> |
4 | #include <api/fd/array.h> |
5 | #include <api/fs/fs.h> |
6 | #include <linux/zalloc.h> |
7 | #include <linux/string.h> |
8 | #include <linux/limits.h> |
9 | #include <string.h> |
10 | #include <sys/file.h> |
11 | #include <signal.h> |
12 | #include <stdlib.h> |
13 | #include <time.h> |
14 | #include <stdio.h> |
15 | #include <unistd.h> |
16 | #include <errno.h> |
17 | #include <sys/inotify.h> |
18 | #include <libgen.h> |
19 | #include <sys/types.h> |
20 | #include <sys/socket.h> |
21 | #include <sys/un.h> |
22 | #include <sys/stat.h> |
23 | #include <sys/signalfd.h> |
24 | #include <sys/wait.h> |
25 | #include <poll.h> |
26 | #include "builtin.h" |
27 | #include "perf.h" |
28 | #include "debug.h" |
29 | #include "config.h" |
30 | #include "util.h" |
31 | |
32 | #define SESSION_OUTPUT "output" |
33 | #define SESSION_CONTROL "control" |
34 | #define SESSION_ACK "ack" |
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; |
77 | char *control; |
78 | int pid; |
79 | struct list_head list; |
80 | enum daemon_session_state state; |
81 | time_t start; |
82 | }; |
83 | |
84 | struct daemon { |
85 | const char *config; |
86 | char *config_real; |
87 | char *config_base; |
88 | const char *csv_sep; |
89 | const char *base_user; |
90 | char *base; |
91 | struct list_head sessions; |
92 | FILE *out; |
93 | char *perf; |
94 | int signal_fd; |
95 | time_t start; |
96 | }; |
97 | |
98 | static struct daemon __daemon = { |
99 | .sessions = LIST_HEAD_INIT(__daemon.sessions), |
100 | }; |
101 | |
102 | static const char * const daemon_usage[] = { |
103 | "perf daemon {start|signal|stop|ping} [<options>]" , |
104 | "perf daemon [<options>]" , |
105 | NULL |
106 | }; |
107 | |
108 | static volatile sig_atomic_t done; |
109 | |
110 | static void sig_handler(int sig __maybe_unused) |
111 | { |
112 | done = true; |
113 | } |
114 | |
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 | |
161 | if (get_session_name(var, session: name, len: sizeof(name) - 1)) |
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(config: 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 | zfree(&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(str: var, prefix: "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 | |
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 | |
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 | |
338 | session->start = time(NULL); |
339 | |
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 | |
373 | if (mkfifo(SESSION_CONTROL, 0600) && errno != EEXIST) { |
374 | perror("failed: create control fifo" ); |
375 | return -1; |
376 | } |
377 | |
378 | if (mkfifo(SESSION_ACK, 0600) && errno != EEXIST) { |
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); |
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 | |
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 | |
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 | */ |
408 | err = read(daemon->signal_fd, &si, sizeof(struct signalfd_siginfo)); |
409 | if (err != sizeof(struct signalfd_siginfo)) { |
410 | pr_err("failed to read signal fd\n" ); |
411 | return -1; |
412 | } |
413 | |
414 | list_for_each_entry(session, &daemon->sessions, list) { |
415 | if (session->pid == -1) |
416 | continue; |
417 | |
418 | pid = waitpid(session->pid, &status, WNOHANG); |
419 | if (pid <= 0) |
420 | continue; |
421 | |
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); |
434 | } |
435 | |
436 | session->state = KILL; |
437 | session->pid = -1; |
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 | }; |
450 | time_t start; |
451 | |
452 | start = time(NULL); |
453 | |
454 | do { |
455 | int err = poll(&pollfd, 1, 1000); |
456 | |
457 | if (err > 0) { |
458 | handle_signalfd(daemon); |
459 | } else if (err < 0) { |
460 | perror("failed: poll\n" ); |
461 | return -1; |
462 | } |
463 | |
464 | if (start + secs < time(NULL)) |
465 | return -1; |
466 | } while (session->pid != -1); |
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 | |
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 | |
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 | |
627 | enum { |
628 | CMD_LIST = 0, |
629 | CMD_SIGNAL = 1, |
630 | CMD_STOP = 2, |
631 | CMD_PING = 3, |
632 | CMD_MAX, |
633 | }; |
634 | |
635 | #define SESSION_MAX 64 |
636 | |
637 | union cmd { |
638 | int cmd; |
639 | |
640 | /* CMD_LIST */ |
641 | struct { |
642 | int cmd; |
643 | int verbose; |
644 | char csv_sep; |
645 | } list; |
646 | |
647 | /* CMD_SIGNAL */ |
648 | struct { |
649 | int cmd; |
650 | int sig; |
651 | char name[SESSION_MAX]; |
652 | } signal; |
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, |
665 | }; |
666 | |
667 | static int daemon_session__ping(struct daemon_session *session) |
668 | { |
669 | return daemon_session__control(session, msg: "ping" , do_ack: true) ? PING_FAIL : PING_OK; |
670 | } |
671 | |
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; |
676 | time_t curr = time(NULL); |
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 | |
687 | fprintf(out, "%c%s/%s" , |
688 | /* lock */ |
689 | csv_sep, daemon->base, "lock" ); |
690 | |
691 | fprintf(out, "%c%lu" , |
692 | /* session up time */ |
693 | csv_sep, (curr - daemon->start) / 60); |
694 | |
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); |
701 | fprintf(out, " lock: %s/lock\n" , |
702 | daemon->base); |
703 | fprintf(out, " up: %lu minutes\n" , |
704 | (curr - daemon->start) / 60); |
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 | |
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 | |
730 | fprintf(out, "%c%lu" , |
731 | /* session up time */ |
732 | csv_sep, (curr - session->start) / 60); |
733 | |
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); |
744 | fprintf(out, " control: %s/%s\n" , |
745 | session->base, SESSION_CONTROL); |
746 | fprintf(out, " ack: %s/%s\n" , |
747 | session->base, SESSION_ACK); |
748 | fprintf(out, " up: %lu minutes\n" , |
749 | (curr - session->start) / 60); |
750 | } |
751 | } |
752 | |
753 | return 0; |
754 | } |
755 | |
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 | |
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 | |
809 | static int handle_server_socket(struct daemon *daemon, int sock_fd) |
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) { |
833 | case CMD_LIST: |
834 | ret = cmd_session_list(daemon, &cmd, out); |
835 | break; |
836 | case CMD_SIGNAL: |
837 | ret = cmd_session_kill(daemon, &cmd, out); |
838 | break; |
839 | case CMD_STOP: |
840 | done = 1; |
841 | ret = 0; |
842 | pr_debug("perf daemon is exciting\n" ); |
843 | break; |
844 | case CMD_PING: |
845 | ret = cmd_session_ping(daemon, &cmd, out); |
846 | break; |
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 | |
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 | |
891 | static void daemon_session__kill(struct daemon_session *session, |
892 | struct daemon *daemon) |
893 | { |
894 | int how = 0; |
895 | |
896 | do { |
897 | switch (how) { |
898 | case 0: |
899 | daemon_session__control(session, msg: "stop" , do_ack: 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: |
908 | pr_err("failed to wait for session %s\n" , |
909 | session->name); |
910 | return; |
911 | } |
912 | how++; |
913 | |
914 | } while (daemon_session__wait(session, daemon, secs: 10)); |
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 | zfree(&session->base); |
928 | zfree(&session->name); |
929 | zfree(&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 | |
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 | |
947 | static void daemon__kill(struct daemon *daemon) |
948 | { |
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: |
963 | pr_err("failed to wait for sessions\n" ); |
964 | return; |
965 | } |
966 | how++; |
967 | |
968 | } while (daemon__wait(daemon, secs: 10)); |
969 | } |
970 | |
971 | static void daemon__exit(struct daemon *daemon) |
972 | { |
973 | struct daemon_session *session, *h; |
974 | |
975 | list_for_each_entry_safe(session, h, &daemon->sessions, list) |
976 | daemon_session__remove(session); |
977 | |
978 | zfree(&daemon->config_real); |
979 | zfree(&daemon->config_base); |
980 | zfree(&daemon->base); |
981 | } |
982 | |
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) { |
995 | daemon_session__kill(session, daemon); |
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) { |
1004 | daemon_session__kill(session, daemon); |
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 | |
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 | |
1094 | static int setup_config(struct daemon *daemon) |
1095 | { |
1096 | if (daemon->base_user) { |
1097 | daemon->base = strdup(daemon->base_user); |
1098 | if (!daemon->base) |
1099 | return -ENOMEM; |
1100 | } |
1101 | |
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 | |
1121 | #ifndef F_TLOCK |
1122 | #define F_TLOCK 2 |
1123 | |
1124 | static int lockf(int fd, int cmd, off_t len) |
1125 | { |
1126 | if (cmd != F_TLOCK || len != 0) |
1127 | return -1; |
1128 | |
1129 | return flock(fd, LOCK_EX | LOCK_NB); |
1130 | } |
1131 | #endif // F_TLOCK |
1132 | |
1133 | /* |
1134 | * Each daemon tries to create and lock BASE/lock file, |
1135 | * if it's successful we are sure we're the only daemon |
1136 | * running over the BASE. |
1137 | * |
1138 | * Once daemon is finished, file descriptor to lock file |
1139 | * is closed and lock is released. |
1140 | */ |
1141 | static int check_lock(struct daemon *daemon) |
1142 | { |
1143 | char path[PATH_MAX]; |
1144 | char buf[20]; |
1145 | int fd, pid; |
1146 | ssize_t len; |
1147 | |
1148 | scnprintf(path, sizeof(path), "%s/lock" , daemon->base); |
1149 | |
1150 | fd = open(path, O_RDWR|O_CREAT|O_CLOEXEC, 0640); |
1151 | if (fd < 0) |
1152 | return -1; |
1153 | |
1154 | if (lockf(fd, F_TLOCK, len: 0) < 0) { |
1155 | filename__read_int(path, &pid); |
1156 | fprintf(stderr, "failed: another perf daemon (pid %d) owns %s\n" , |
1157 | pid, daemon->base); |
1158 | close(fd); |
1159 | return -1; |
1160 | } |
1161 | |
1162 | scnprintf(buf, sizeof(buf), "%d" , getpid()); |
1163 | len = strlen(buf); |
1164 | |
1165 | if (write(fd, buf, len) != len) { |
1166 | perror("failed: write" ); |
1167 | close(fd); |
1168 | return -1; |
1169 | } |
1170 | |
1171 | if (ftruncate(fd, len)) { |
1172 | perror("failed: ftruncate" ); |
1173 | close(fd); |
1174 | return -1; |
1175 | } |
1176 | |
1177 | return 0; |
1178 | } |
1179 | |
1180 | static int go_background(struct daemon *daemon) |
1181 | { |
1182 | int pid, fd; |
1183 | |
1184 | pid = fork(); |
1185 | if (pid < 0) |
1186 | return -1; |
1187 | |
1188 | if (pid > 0) |
1189 | return 1; |
1190 | |
1191 | if (setsid() < 0) |
1192 | return -1; |
1193 | |
1194 | if (check_lock(daemon)) |
1195 | return -1; |
1196 | |
1197 | umask(0); |
1198 | |
1199 | if (chdir(daemon->base)) { |
1200 | perror("failed: chdir" ); |
1201 | return -1; |
1202 | } |
1203 | |
1204 | fd = open("output" , O_RDWR|O_CREAT|O_TRUNC, 0644); |
1205 | if (fd < 0) { |
1206 | perror("failed: open" ); |
1207 | return -1; |
1208 | } |
1209 | |
1210 | if (fcntl(fd, F_SETFD, FD_CLOEXEC)) { |
1211 | perror("failed: fcntl FD_CLOEXEC" ); |
1212 | close(fd); |
1213 | return -1; |
1214 | } |
1215 | |
1216 | close(0); |
1217 | dup2(fd, 1); |
1218 | dup2(fd, 2); |
1219 | close(fd); |
1220 | |
1221 | daemon->out = fdopen(1, "w" ); |
1222 | if (!daemon->out) { |
1223 | close(1); |
1224 | close(2); |
1225 | return -1; |
1226 | } |
1227 | |
1228 | setbuf(daemon->out, NULL); |
1229 | return 0; |
1230 | } |
1231 | |
1232 | static int setup_signalfd(struct daemon *daemon) |
1233 | { |
1234 | sigset_t mask; |
1235 | |
1236 | sigemptyset(&mask); |
1237 | sigaddset(&mask, SIGCHLD); |
1238 | |
1239 | if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) |
1240 | return -1; |
1241 | |
1242 | daemon->signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); |
1243 | return daemon->signal_fd; |
1244 | } |
1245 | |
1246 | static int __cmd_start(struct daemon *daemon, struct option parent_options[], |
1247 | int argc, const char **argv) |
1248 | { |
1249 | bool foreground = false; |
1250 | struct option start_options[] = { |
1251 | OPT_BOOLEAN('f', "foreground" , &foreground, "stay on console" ), |
1252 | OPT_PARENT(parent_options), |
1253 | OPT_END() |
1254 | }; |
1255 | int sock_fd = -1, conf_fd = -1, signal_fd = -1; |
1256 | int sock_pos, file_pos, signal_pos; |
1257 | struct fdarray fda; |
1258 | int err = 0; |
1259 | |
1260 | argc = parse_options(argc, argv, start_options, daemon_usage, 0); |
1261 | if (argc) |
1262 | usage_with_options(daemon_usage, start_options); |
1263 | |
1264 | daemon->start = time(NULL); |
1265 | |
1266 | if (setup_config(daemon)) { |
1267 | pr_err("failed: config not found\n" ); |
1268 | return -1; |
1269 | } |
1270 | |
1271 | if (setup_server_config(daemon)) |
1272 | return -1; |
1273 | |
1274 | if (foreground && check_lock(daemon)) |
1275 | return -1; |
1276 | |
1277 | if (!foreground) { |
1278 | err = go_background(daemon); |
1279 | if (err) { |
1280 | /* original process, exit normally */ |
1281 | if (err == 1) |
1282 | err = 0; |
1283 | daemon__exit(daemon); |
1284 | return err; |
1285 | } |
1286 | } |
1287 | |
1288 | debug_set_file(daemon->out); |
1289 | debug_set_display_time(true); |
1290 | |
1291 | pr_info("daemon started (pid %d)\n" , getpid()); |
1292 | |
1293 | fdarray__init(&fda, 3); |
1294 | |
1295 | sock_fd = setup_server_socket(daemon); |
1296 | if (sock_fd < 0) |
1297 | goto out; |
1298 | |
1299 | conf_fd = setup_config_changes(daemon); |
1300 | if (conf_fd < 0) |
1301 | goto out; |
1302 | |
1303 | signal_fd = setup_signalfd(daemon); |
1304 | if (signal_fd < 0) |
1305 | goto out; |
1306 | |
1307 | sock_pos = fdarray__add(&fda, sock_fd, POLLIN|POLLERR|POLLHUP, 0); |
1308 | if (sock_pos < 0) |
1309 | goto out; |
1310 | |
1311 | file_pos = fdarray__add(&fda, conf_fd, POLLIN|POLLERR|POLLHUP, 0); |
1312 | if (file_pos < 0) |
1313 | goto out; |
1314 | |
1315 | signal_pos = fdarray__add(&fda, signal_fd, POLLIN|POLLERR|POLLHUP, 0); |
1316 | if (signal_pos < 0) |
1317 | goto out; |
1318 | |
1319 | signal(SIGINT, sig_handler); |
1320 | signal(SIGTERM, sig_handler); |
1321 | signal(SIGPIPE, SIG_IGN); |
1322 | |
1323 | while (!done && !err) { |
1324 | err = daemon__reconfig(daemon); |
1325 | |
1326 | if (!err && fdarray__poll(&fda, -1)) { |
1327 | bool reconfig = false; |
1328 | |
1329 | if (fda.entries[sock_pos].revents & POLLIN) |
1330 | err = handle_server_socket(daemon, sock_fd); |
1331 | if (fda.entries[file_pos].revents & POLLIN) |
1332 | err = handle_config_changes(daemon, conf_fd, config_changed: &reconfig); |
1333 | if (fda.entries[signal_pos].revents & POLLIN) |
1334 | err = handle_signalfd(daemon) < 0; |
1335 | |
1336 | if (reconfig) |
1337 | err = setup_server_config(daemon); |
1338 | } |
1339 | } |
1340 | |
1341 | out: |
1342 | fdarray__exit(&fda); |
1343 | |
1344 | daemon__kill(daemon); |
1345 | daemon__exit(daemon); |
1346 | |
1347 | if (sock_fd != -1) |
1348 | close(sock_fd); |
1349 | if (conf_fd != -1) |
1350 | close(conf_fd); |
1351 | if (signal_fd != -1) |
1352 | close(signal_fd); |
1353 | |
1354 | pr_info("daemon exited\n" ); |
1355 | fclose(daemon->out); |
1356 | return err; |
1357 | } |
1358 | |
1359 | static int send_cmd(struct daemon *daemon, union cmd *cmd) |
1360 | { |
1361 | int ret = -1, fd; |
1362 | char *line = NULL; |
1363 | size_t len = 0; |
1364 | ssize_t nread; |
1365 | FILE *in = NULL; |
1366 | |
1367 | if (setup_client_config(daemon)) |
1368 | return -1; |
1369 | |
1370 | fd = setup_client_socket(daemon); |
1371 | if (fd < 0) |
1372 | return -1; |
1373 | |
1374 | if (sizeof(*cmd) != writen(fd, cmd, sizeof(*cmd))) { |
1375 | perror("failed: write" ); |
1376 | goto out; |
1377 | } |
1378 | |
1379 | in = fdopen(fd, "r" ); |
1380 | if (!in) { |
1381 | perror("failed: fdopen" ); |
1382 | goto out; |
1383 | } |
1384 | |
1385 | while ((nread = getline(&line, &len, in)) != -1) { |
1386 | if (fwrite(line, nread, 1, stdout) != 1) |
1387 | goto out_fclose; |
1388 | fflush(stdout); |
1389 | } |
1390 | |
1391 | ret = 0; |
1392 | out_fclose: |
1393 | fclose(in); |
1394 | free(line); |
1395 | out: |
1396 | /* If in is defined, then fd is closed via fclose. */ |
1397 | if (!in) |
1398 | close(fd); |
1399 | return ret; |
1400 | } |
1401 | |
1402 | static int send_cmd_list(struct daemon *daemon) |
1403 | { |
1404 | union cmd cmd; |
1405 | |
1406 | memset(&cmd, 0, sizeof(cmd)); |
1407 | cmd.list.cmd = CMD_LIST; |
1408 | cmd.list.verbose = verbose; |
1409 | cmd.list.csv_sep = daemon->csv_sep ? *daemon->csv_sep : 0; |
1410 | |
1411 | return send_cmd(daemon, cmd: &cmd); |
1412 | } |
1413 | |
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 | memset(&cmd, 0, sizeof(cmd)); |
1436 | cmd.signal.cmd = CMD_SIGNAL, |
1437 | cmd.signal.sig = SIGUSR2; |
1438 | strncpy(p: cmd.signal.name, q: name, size: sizeof(cmd.signal.name) - 1); |
1439 | |
1440 | return send_cmd(daemon, cmd: &cmd); |
1441 | } |
1442 | |
1443 | static int __cmd_stop(struct daemon *daemon, struct option parent_options[], |
1444 | int argc, const char **argv) |
1445 | { |
1446 | struct option start_options[] = { |
1447 | OPT_PARENT(parent_options), |
1448 | OPT_END() |
1449 | }; |
1450 | union cmd cmd; |
1451 | |
1452 | argc = parse_options(argc, argv, start_options, daemon_usage, 0); |
1453 | if (argc) |
1454 | usage_with_options(daemon_usage, start_options); |
1455 | |
1456 | if (setup_config(daemon)) { |
1457 | pr_err("failed: config not found\n" ); |
1458 | return -1; |
1459 | } |
1460 | |
1461 | memset(&cmd, 0, sizeof(cmd)); |
1462 | cmd.cmd = CMD_STOP; |
1463 | return send_cmd(daemon, cmd: &cmd); |
1464 | } |
1465 | |
1466 | static int __cmd_ping(struct daemon *daemon, struct option parent_options[], |
1467 | int argc, const char **argv) |
1468 | { |
1469 | const char *name = "all" ; |
1470 | struct option ping_options[] = { |
1471 | OPT_STRING(0, "session" , &name, "session" , |
1472 | "Ping to specific session" ), |
1473 | OPT_PARENT(parent_options), |
1474 | OPT_END() |
1475 | }; |
1476 | union cmd cmd; |
1477 | |
1478 | argc = parse_options(argc, argv, ping_options, daemon_usage, 0); |
1479 | if (argc) |
1480 | usage_with_options(daemon_usage, ping_options); |
1481 | |
1482 | if (setup_config(daemon)) { |
1483 | pr_err("failed: config not found\n" ); |
1484 | return -1; |
1485 | } |
1486 | |
1487 | memset(&cmd, 0, sizeof(cmd)); |
1488 | cmd.cmd = CMD_PING; |
1489 | scnprintf(cmd.ping.name, sizeof(cmd.ping.name), "%s" , name); |
1490 | return send_cmd(daemon, cmd: &cmd); |
1491 | } |
1492 | |
1493 | static char *alloc_perf_exe_path(void) |
1494 | { |
1495 | char path[PATH_MAX]; |
1496 | |
1497 | perf_exe(path, sizeof(path)); |
1498 | return strdup(path); |
1499 | } |
1500 | |
1501 | int cmd_daemon(int argc, const char **argv) |
1502 | { |
1503 | struct option daemon_options[] = { |
1504 | OPT_INCR('v', "verbose" , &verbose, "be more verbose" ), |
1505 | OPT_STRING(0, "config" , &__daemon.config, |
1506 | "config file" , "config file path" ), |
1507 | OPT_STRING(0, "base" , &__daemon.base_user, |
1508 | "directory" , "base directory" ), |
1509 | OPT_STRING_OPTARG('x', "field-separator" , &__daemon.csv_sep, |
1510 | "field separator" , "print counts with custom separator" , "," ), |
1511 | OPT_END() |
1512 | }; |
1513 | int ret = -1; |
1514 | |
1515 | __daemon.perf = alloc_perf_exe_path(); |
1516 | if (!__daemon.perf) |
1517 | return -ENOMEM; |
1518 | |
1519 | __daemon.out = stdout; |
1520 | |
1521 | argc = parse_options(argc, argv, daemon_options, daemon_usage, |
1522 | PARSE_OPT_STOP_AT_NON_OPTION); |
1523 | |
1524 | if (argc) { |
1525 | if (!strcmp(argv[0], "start" )) |
1526 | ret = __cmd_start(&__daemon, daemon_options, argc, argv); |
1527 | else if (!strcmp(argv[0], "signal" )) |
1528 | ret = __cmd_signal(&__daemon, daemon_options, argc, argv); |
1529 | else if (!strcmp(argv[0], "stop" )) |
1530 | ret = __cmd_stop(&__daemon, daemon_options, argc, argv); |
1531 | else if (!strcmp(argv[0], "ping" )) |
1532 | ret = __cmd_ping(&__daemon, daemon_options, argc, argv); |
1533 | else |
1534 | pr_err("failed: unknown command '%s'\n" , argv[0]); |
1535 | } else { |
1536 | ret = setup_config(&__daemon); |
1537 | if (ret) |
1538 | pr_err("failed: config not found\n" ); |
1539 | else |
1540 | ret = send_cmd_list(daemon: &__daemon); |
1541 | } |
1542 | zfree(&__daemon.perf); |
1543 | return ret; |
1544 | } |
1545 | |