Add foreground option to daemon
[guacd.git] / src / daemon.c
1
2 /* ***** BEGIN LICENSE BLOCK *****
3  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4  *
5  * The contents of this file are subject to the Mozilla Public License Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  * http://www.mozilla.org/MPL/
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the
13  * License.
14  *
15  * The Original Code is guacd.
16  *
17  * The Initial Developer of the Original Code is
18  * Michael Jumper.
19  * Portions created by the Initial Developer are Copyright (C) 2010
20  * the Initial Developer. All Rights Reserved.
21  *
22  * Contributor(s):
23  *  David PHAM-VAN <d.pham-van@ulteo.com> Ulteo SAS - http://www.ulteo.com
24  *
25  * Alternatively, the contents of this file may be used under the terms of
26  * either the GNU General Public License Version 2 or later (the "GPL"), or
27  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28  * in which case the provisions of the GPL or the LGPL are applicable instead
29  * of those above. If you wish to allow use of your version of this file only
30  * under the terms of either the GPL or the LGPL, and not to allow others to
31  * use your version of this file under the terms of the MPL, indicate your
32  * decision by deleting the provisions above and replace them with the notice
33  * and other provisions required by the GPL or the LGPL. If you do not delete
34  * the provisions above, a recipient may use your version of this file under
35  * the terms of any one of the MPL, the GPL or the LGPL.
36  *
37  * ***** END LICENSE BLOCK ***** */
38
39 #include <unistd.h>
40 #include <stdlib.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <signal.h>
44 #include <sys/types.h>
45
46 #include <sys/socket.h>
47 #include <netdb.h>
48 #include <netinet/in.h>
49
50 #include <errno.h>
51 #include <syslog.h>
52 #include <libgen.h>
53
54 #include <guacamole/client.h>
55 #include <guacamole/error.h>
56
57 #include "client.h"
58 #include "log.h"
59
60 void guacd_handle_connection(int fd) {
61
62     guac_client* client;
63     guac_client_plugin* plugin;
64     guac_instruction* select;
65     guac_instruction* connect;
66
67     /* Open guac_socket */
68     guac_socket* socket = guac_socket_open(fd);
69
70     /* Get protocol from select instruction */
71     select = guac_protocol_expect_instruction(
72             socket, GUACD_USEC_TIMEOUT, "select");
73     if (select == NULL) {
74
75         /* Log error */
76         guacd_log_guac_error("Error reading \"select\"");
77
78         /* Free resources */
79         guac_socket_close(socket);
80         return;
81     }
82
83     /* Validate args to select */
84     if (select->argc != 1) {
85
86         /* Log error */
87         guacd_log_error("Bad number of arguments to \"select\" (%i)",
88                 select->argc);
89
90         /* Free resources */
91         guac_socket_close(socket);
92         return;
93     }
94
95     guacd_log_info("Protocol \"%s\" selected", select->argv[0]);
96
97     /* Get plugin from protocol in select */
98     plugin = guac_client_plugin_open(select->argv[0]);
99     guac_instruction_free(select);
100
101     if (plugin == NULL) {
102
103         /* Log error */
104         guacd_log_guac_error("Error loading client plugin");
105
106         /* Free resources */
107         guac_socket_close(socket);
108         return;
109     }
110
111     /* Send args response */
112     if (guac_protocol_send_args(socket, plugin->args)
113             || guac_socket_flush(socket)) {
114
115         /* Log error */
116         guacd_log_guac_error("Error sending \"args\"");
117
118         if (guac_client_plugin_close(plugin))
119             guacd_log_guac_error("Error closing client plugin");
120
121         guac_socket_close(socket);
122         return;
123     }
124
125     /* Get args from connect instruction */
126     connect = guac_protocol_expect_instruction(
127             socket, GUACD_USEC_TIMEOUT, "connect");
128     if (connect == NULL) {
129
130         /* Log error */
131         guacd_log_guac_error("Error reading \"connect\"");
132
133         if (guac_client_plugin_close(plugin))
134             guacd_log_guac_error("Error closing client plugin");
135
136         guac_socket_close(socket);
137         return;
138     }
139
140     /* Load and init client */
141     client = guac_client_plugin_get_client(plugin, socket,
142             connect->argc, connect->argv,
143             guacd_client_log_info, guacd_client_log_error);
144
145     guac_instruction_free(connect);
146
147     if (client == NULL) {
148
149         guacd_log_guac_error("Error instantiating client");
150
151         if (guac_client_plugin_close(plugin))
152             guacd_log_guac_error("Error closing client plugin");
153
154         guac_socket_close(socket);
155         return;
156     }
157
158     /* Start client threads */
159     guacd_log_info("Starting client");
160     if (guacd_client_start(client))
161         guacd_log_error("Client finished abnormally");
162     else
163         guacd_log_info("Client finished normally");
164
165     /* Clean up */
166     guac_client_free(client);
167     if (guac_client_plugin_close(plugin))
168         guacd_log_error("Error closing client plugin");
169
170     /* Close socket */
171     guac_socket_close(socket);
172
173     return;
174
175 }
176
177 int main(int argc, char* argv[]) {
178
179     /* Server */
180     int socket_fd;
181     struct addrinfo* addresses;
182     struct addrinfo* current_address;
183     char bound_address[1024];
184     char bound_port[64];
185     int opt_on = 1;
186
187     struct addrinfo hints = {
188         .ai_family   = AF_UNSPEC,
189         .ai_socktype = SOCK_STREAM,
190         .ai_protocol = IPPROTO_TCP
191     };
192
193     /* Client */
194     struct sockaddr_in client_addr;
195     socklen_t client_addr_len;
196     int connected_socket_fd;
197
198     /* Arguments */
199     char* listen_address = NULL; /* Default address of INADDR_ANY */
200     char* listen_port = "4822";  /* Default port */
201     char* pidfile = NULL;
202     int opt;
203     int foreground = 0;
204
205     /* General */
206     int retval;
207
208     /* Daemon Process */
209     pid_t daemon_pid;
210
211     /* Parse arguments */
212     while ((opt = getopt(argc, argv, "l:b:p:f")) != -1) {
213         if (opt == 'l') {
214             listen_port = strdup(optarg);
215         }
216         else if (opt == 'b') {
217             listen_address = strdup(optarg);
218         }
219         else if (opt == 'f') {
220             foreground = 1;
221         }
222         else if (opt == 'p') {
223             pidfile = strdup(optarg);
224         }
225         else {
226
227             fprintf(stderr, "USAGE: %s"
228                     " [-l LISTENPORT]"
229                     " [-b LISTENADDRESS]"
230                     " [-f foreground]"
231                     " [-p PIDFILE]\n", argv[0]);
232
233             exit(EXIT_FAILURE);
234         }
235     }
236
237     /* Set up logging prefix */
238     strncpy(log_prefix, basename(argv[0]), sizeof(log_prefix));
239
240
241     /* Get addresses for binding */
242     if ((retval = getaddrinfo(listen_address, listen_port, &hints, &addresses))) {
243         guacd_log_error("Error parsing given address or port: %s",
244                 gai_strerror(retval));
245         exit(EXIT_FAILURE);
246     }
247
248     /* Get socket */
249     socket_fd = socket(AF_INET, SOCK_STREAM, 0);
250     if (socket_fd < 0) {
251         guacd_log_error("Error opening socket: %s", strerror(errno));
252         exit(EXIT_FAILURE);
253     }
254
255     /* Allow socket reuse */
256     if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, (void*) &opt_on, sizeof(opt_on))) {
257         guacd_log_info("Unable to set socket options for reuse: %s", strerror(errno));
258     }
259
260     /* Attempt binding of each address until success */
261     current_address = addresses;
262     while (current_address != NULL) {
263
264         int retval;
265
266         /* Resolve hostname */
267         if ((retval = getnameinfo(current_address->ai_addr,
268                 current_address->ai_addrlen,
269                 bound_address, sizeof(bound_address),
270                 bound_port, sizeof(bound_port),
271                 NI_NUMERICHOST | NI_NUMERICSERV)))
272             guacd_log_error("Unable to resolve host: %s",
273                     gai_strerror(retval));
274
275         /* Attempt to bind socket to address */
276         if (bind(socket_fd,
277                     current_address->ai_addr,
278                     current_address->ai_addrlen) == 0) {
279
280             guacd_log_info("Successfully bound socket to "
281                     "host %s, port %s", bound_address, bound_port);
282
283             /* Done if successful bind */
284             break;
285
286         }
287
288         /* Otherwise log information regarding bind failure */
289         else
290             guacd_log_info("Unable to bind socket to "
291                     "host %s, port %s: %s",
292                     bound_address, bound_port, strerror(errno));
293
294         current_address = current_address->ai_next;
295
296     }
297
298     /* If unable to bind to anything, fail */
299     if (current_address == NULL) {
300         guacd_log_error("Unable to bind socket to any addresses.");
301         exit(EXIT_FAILURE);
302     }
303
304     if (!foreground) {
305         /* Fork into background */
306         daemon_pid = fork();
307
308         /* If error, fail */
309         if (daemon_pid == -1) {
310             guacd_log_error("Error forking daemon process: %s", strerror(errno));
311             exit(EXIT_FAILURE);
312         }
313
314         /* If parent, write PID file and exit */
315         else if (daemon_pid != 0) {
316
317             if (pidfile != NULL) {
318
319                 /* Attempt to open pidfile and write PID */
320                 FILE* pidf = fopen(pidfile, "w");
321                 if (pidf) {
322                     fprintf(pidf, "%d\n", daemon_pid);
323                     fclose(pidf);
324                 }
325
326                 /* Warn on failure */
327                 else {
328                     guacd_log_error("Could not write PID file: %s", strerror(errno));
329                     exit(EXIT_FAILURE);
330                 }
331
332             }
333
334             exit(EXIT_SUCCESS);
335         }
336     }
337
338     /* Open log */
339     openlog(NULL, LOG_PID, LOG_DAEMON);
340
341     /* Ignore SIGPIPE */
342     if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
343         guacd_log_info("Could not set handler for SIGPIPE to ignore. SIGPIPE may cause termination of the daemon.");
344     }
345
346     /* Ignore SIGCHLD (force automatic removal of children) */
347     if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) {
348         guacd_log_info("Could not set handler for SIGCHLD to ignore. Child processes may pile up in the process table.");
349     }
350
351     /* Log listening status */
352     syslog(LOG_INFO,
353             "Listening on host %s, port %s", bound_address, bound_port);
354
355     /* Free addresses */
356     freeaddrinfo(addresses);
357
358     /* Daemon loop */
359     for (;;) {
360
361         pid_t child_pid;
362
363         /* Listen for connections */
364         if (listen(socket_fd, 5) < 0) {
365             guacd_log_error("Could not listen on socket: %s", strerror(errno));
366             return 3;
367         }
368
369         /* Accept connection */
370         client_addr_len = sizeof(client_addr);
371         connected_socket_fd = accept(socket_fd, (struct sockaddr*) &client_addr, &client_addr_len);
372         if (connected_socket_fd < 0) {
373             guacd_log_error("Could not accept client connection: %s", strerror(errno));
374             return 3;
375         }
376
377         /* 
378          * Once connection is accepted, send child into background.
379          *
380          * Note that we prefer fork() over threads for connection-handling
381          * processes as they give each connection its own memory area, and
382          * isolate the main daemon and other connections from errors in any
383          * particular client plugin.
384          */
385
386         child_pid = fork();
387
388         /* If error, log */
389         if (child_pid == -1)
390             guacd_log_error("Error forking child process: %s", strerror(errno));
391
392         /* If child, start client, and exit when finished */
393         else if (child_pid == 0) {
394             guacd_handle_connection(connected_socket_fd);
395             close(connected_socket_fd);
396             return 0;
397         }
398
399         /* If parent, close reference to child's descriptor */
400         else if (close(connected_socket_fd) < 0) {
401             guacd_log_error("Error closing daemon reference to child descriptor: %s", strerror(errno));
402         }
403
404     }
405
406     /* Close socket */
407     if (close(socket_fd) < 0) {
408         guacd_log_error("Could not close socket: %s", strerror(errno));
409         return 3;
410     }
411
412     return 0;
413
414 }
415