Fix compile time issues
[collectd.git] / src / daemon / cmd.c
1 /**
2  * collectd - src/daemon/cmd.c
3  * Copyright (C) 2005-2007  Florian octo Forster
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  **/
23
24 #include "cmd.h"
25 #include "collectd.h"
26
27 #include "utils/common/common.h"
28 #include <sys/un.h>
29
30 static void *do_flush(void __attribute__((unused)) * arg) {
31   INFO("Flushing all data.");
32   plugin_flush(/* plugin = */ NULL,
33                /* timeout = */ 0,
34                /* ident = */ NULL);
35   INFO("Finished flushing all data.");
36   pthread_exit(NULL);
37   return NULL;
38 }
39
40 static void sig_int_handler(int __attribute__((unused)) signal) {
41   stop_collectd();
42 }
43
44 static void sig_term_handler(int __attribute__((unused)) signal) {
45   stop_collectd();
46 }
47
48 static void sig_usr1_handler(int __attribute__((unused)) signal) {
49   pthread_t thread;
50   pthread_attr_t attr;
51
52   /* flushing the data might take a while,
53    * so it should be done asynchronously */
54   pthread_attr_init(&attr);
55   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
56   pthread_create(&thread, &attr, do_flush, NULL);
57   pthread_attr_destroy(&attr);
58 }
59
60 #if COLLECT_DAEMON
61 static int pidfile_create(void) {
62   FILE *fh;
63   const char *file = global_option_get("PIDFile");
64
65   if ((fh = fopen(file, "w")) == NULL) {
66     ERROR("fopen (%s): %s", file, STRERRNO);
67     return 1;
68   }
69
70   fprintf(fh, "%i\n", (int)getpid());
71   fclose(fh);
72
73   return 0;
74 } /* static int pidfile_create (const char *file) */
75
76 static int pidfile_remove(void) {
77   const char *file = global_option_get("PIDFile");
78   if (file == NULL)
79     return 0;
80
81   return unlink(file);
82 } /* static int pidfile_remove (const char *file) */
83 #endif /* COLLECT_DAEMON */
84
85 #ifdef KERNEL_LINUX
86 static int notify_upstart(void) {
87   char const *upstart_job = getenv("UPSTART_JOB");
88
89   if (upstart_job == NULL)
90     return 0;
91
92   if (strcmp(upstart_job, "collectd") != 0) {
93     WARNING("Environment specifies unexpected UPSTART_JOB=\"%s\", expected "
94             "\"collectd\". Ignoring the variable.",
95             upstart_job);
96     return 0;
97   }
98
99   NOTICE("Upstart detected, stopping now to signal readiness.");
100   raise(SIGSTOP);
101   unsetenv("UPSTART_JOB");
102
103   return 1;
104 }
105
106 static int notify_systemd(void) {
107   size_t su_size;
108   const char *notifysocket = getenv("NOTIFY_SOCKET");
109   if (notifysocket == NULL)
110     return 0;
111
112   if ((strlen(notifysocket) < 2) ||
113       ((notifysocket[0] != '@') && (notifysocket[0] != '/'))) {
114     ERROR("invalid notification socket NOTIFY_SOCKET=\"%s\": path must be "
115           "absolute",
116           notifysocket);
117     return 0;
118   }
119   NOTICE("Systemd detected, trying to signal readiness.");
120
121   unsetenv("NOTIFY_SOCKET");
122
123   int fd;
124 #if defined(SOCK_CLOEXEC)
125   fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, /* protocol = */ 0);
126 #else
127   fd = socket(AF_UNIX, SOCK_DGRAM, /* protocol = */ 0);
128 #endif
129   if (fd < 0) {
130     ERROR("creating UNIX socket failed: %s", STRERRNO);
131     return 0;
132   }
133
134   struct sockaddr_un su = {0};
135   su.sun_family = AF_UNIX;
136   if (notifysocket[0] != '@') {
137     /* regular UNIX socket */
138     sstrncpy(su.sun_path, notifysocket, sizeof(su.sun_path));
139     su_size = sizeof(su);
140   } else {
141     /* Linux abstract namespace socket: specify address as "\0foo", i.e.
142      * start with a null byte. Since null bytes have no special meaning in
143      * that case, we have to set su_size correctly to cover only the bytes
144      * that are part of the address. */
145     sstrncpy(su.sun_path, notifysocket, sizeof(su.sun_path));
146     su.sun_path[0] = 0;
147     su_size = sizeof(sa_family_t) + strlen(notifysocket);
148     if (su_size > sizeof(su))
149       su_size = sizeof(su);
150   }
151
152   const char buffer[] = "READY=1\n";
153   if (sendto(fd, buffer, strlen(buffer), MSG_NOSIGNAL, (void *)&su,
154              (socklen_t)su_size) < 0) {
155     ERROR("sendto(\"%s\") failed: %s", notifysocket, STRERRNO);
156     close(fd);
157     return 0;
158   }
159
160   unsetenv("NOTIFY_SOCKET");
161   close(fd);
162   return 1;
163 }
164 #endif /* KERNEL_LINUX */
165
166 int main(int argc, char **argv) {
167   struct cmdline_config config = init_config(argc, argv);
168
169 #if COLLECT_DAEMON
170   /*
171    * fork off child
172    */
173   struct sigaction sig_chld_action = {.sa_handler = SIG_IGN};
174
175   sigaction(SIGCHLD, &sig_chld_action, NULL);
176
177   /*
178    * Only daemonize if we're not being supervised
179    * by upstart or systemd (when using Linux).
180    */
181   if (config.daemonize
182 #ifdef KERNEL_LINUX
183       && notify_upstart() == 0 && notify_systemd() == 0
184 #endif
185   ) {
186     pid_t pid;
187     if ((pid = fork()) == -1) {
188       /* error */
189       fprintf(stderr, "fork: %s", STRERRNO);
190       return 1;
191     } else if (pid != 0) {
192       /* parent */
193       /* printf ("Running (PID %i)\n", pid); */
194       return 0;
195     }
196
197     /* Detach from session */
198     setsid();
199
200     /* Write pidfile */
201     if (pidfile_create())
202       exit(2);
203
204     /* close standard descriptors */
205     close(2);
206     close(1);
207     close(0);
208
209     int status = open("/dev/null", O_RDWR);
210     if (status != 0) {
211       ERROR("Error: Could not connect `STDIN' to `/dev/null' (status %d)",
212             status);
213       return 1;
214     }
215
216     status = dup(0);
217     if (status != 1) {
218       ERROR("Error: Could not connect `STDOUT' to `/dev/null' (status %d)",
219             status);
220       return 1;
221     }
222
223     status = dup(0);
224     if (status != 2) {
225       ERROR("Error: Could not connect `STDERR' to `/dev/null', (status %d)",
226             status);
227       return 1;
228     }
229   }    /* if (config.daemonize) */
230 #endif /* COLLECT_DAEMON */
231
232   struct sigaction sig_pipe_action = {.sa_handler = SIG_IGN};
233
234   sigaction(SIGPIPE, &sig_pipe_action, NULL);
235
236   /*
237    * install signal handlers
238    */
239   struct sigaction sig_int_action = {.sa_handler = sig_int_handler};
240
241   if (sigaction(SIGINT, &sig_int_action, NULL) != 0) {
242     ERROR("Error: Failed to install a signal handler for signal INT: %s",
243           STRERRNO);
244     return 1;
245   }
246
247   struct sigaction sig_term_action = {.sa_handler = sig_term_handler};
248
249   if (sigaction(SIGTERM, &sig_term_action, NULL) != 0) {
250     ERROR("Error: Failed to install a signal handler for signal TERM: %s",
251           STRERRNO);
252     return 1;
253   }
254
255   struct sigaction sig_usr1_action = {.sa_handler = sig_usr1_handler};
256
257   if (sigaction(SIGUSR1, &sig_usr1_action, NULL) != 0) {
258     ERROR("Error: Failed to install a signal handler for signal USR1: %s",
259           STRERRNO);
260     return 1;
261   }
262
263   int exit_status = run_loop(config.test_readall);
264
265 #if COLLECT_DAEMON
266   if (config.daemonize)
267     pidfile_remove();
268 #endif /* COLLECT_DAEMON */
269
270   return exit_status;
271 } /* int main */