source: nanoscale/trunk/server.c @ 6609

Last change on this file since 6609 was 6609, checked in by ldelgass, 8 years ago

Update config files with timeout values for geovis,nanovis,vtkvis

File size: 16.4 KB
Line 
1/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/* ======================================================================
3 *  Copyright (c) 2004-2016  HUBzero Foundation, LLC
4 * ----------------------------------------------------------------------
5 *  See the file "license.terms" for information on usage and
6 *  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
7 * ======================================================================
8 */
9#define _GNU_SOURCE
10#include <sys/socket.h>
11
12#include <stdio.h>
13#include <string.h>
14#include <errno.h>
15#include <arpa/inet.h>
16#include <getopt.h>
17#include <stdarg.h>
18#include <stdlib.h>
19#include <sys/wait.h>
20#include <sys/stat.h>
21#include <sys/file.h>
22#include <unistd.h>
23#include <fcntl.h>
24
25#ifdef TCL_CONFIG
26#include <tcl.h>
27#endif
28
29#include "config.h"
30#include "server.h"
31
32static const char *syslogLevels[] = {
33    "emergency",                        /* System is unusable */
34    "alert",                            /* Action must be taken immediately */
35    "critical",                         /* Critical conditions */
36    "error",                            /* Error conditions */
37    "warning",                          /* Warning conditions */
38    "notice",                           /* Normal but significant condition */
39    "info",                             /* Informational */
40    "debug",                            /* Debug-level messages */
41};
42
43static int numServers = 0;
44/* Table of render servers representing services available to
45 * clients.  A new instance is forked and executed each time a
46 * new request is accepted. */
47static RenderServer *serverTable[MAX_SERVERS];
48
49static int debug = FALSE;
50static pid_t serverPid;
51
52static void
53Help(const char *program)
54{
55    fprintf(stderr,
56        "Syntax: %s [-d] [-f serversFile] [-x numVideoCards]\n", program);
57    exit(1);
58}
59
60static void
61InitLog()
62{
63    openlog("nanoscale", LOG_CONS | LOG_PID, LOG_USER);
64}
65
66void
67SysLog(int priority, const char *path, int lineNum, const char* fmt, ...)
68{
69#define MSG_LEN (2047)
70    char message[MSG_LEN+1];
71    const char *s;
72    int length;
73    va_list lst;
74
75    va_start(lst, fmt);
76    s = strrchr(path, '/');
77    if (s == NULL) {
78        s = path;
79    } else {
80        s++;
81    }
82    if (serverPid != getpid()) {
83        length = snprintf(message, MSG_LEN, "(%d) %s: %s:%d ",
84                          serverPid, syslogLevels[priority],  s, lineNum);
85    } else {
86        length = snprintf(message, MSG_LEN, "%s: %s:%d ",
87                          syslogLevels[priority],  s, lineNum);
88    }
89    length += vsnprintf(message + length, MSG_LEN - length, fmt, lst);
90    message[MSG_LEN] = '\0';
91    if (debug) {
92        fprintf(stderr, "%s\n", message);
93    } else {
94        syslog(priority, message, length);
95    }
96}
97
98RenderServer *
99NewServer(const char *name)
100{
101    RenderServer *serverPtr;
102
103    serverPtr = malloc(sizeof(RenderServer));
104    memset(serverPtr, 0, sizeof(RenderServer));
105    if (serverPtr == NULL) {
106        ERROR("can't allocate structure: %s", strerror(errno));
107        return NULL;
108    }
109    if (name != NULL) {
110        serverPtr->name = strdup(name);
111    }
112    serverPtr->outputFd = STDOUT_FILENO;
113    serverPtr->inputFd = STDIN_FILENO;
114    serverPtr->combineLogs = TRUE;
115    serverPtr->logStdout = TRUE;
116    serverPtr->logStderr = TRUE;
117    return serverPtr;
118}
119
120static void
121InitServerTable()
122{
123    memset(serverTable, 0, sizeof(serverTable));
124}
125
126int
127main(int argc, char **argv)
128{
129    int si;
130#ifdef SA_NOCLDWAIT
131    struct sigaction action;
132#endif
133    fd_set serverFds;
134    int maxFd;                          /* Highest file descriptor in use. */
135    char display[200];                  /* String used to manage the X
136                                         * DISPLAY variable for each render
137                                         * server instance. */
138    int maxCards;                       /* Maximum number of video cards, each
139                                         * represented by a different X
140                                         * screen.  */
141    int screenNum;                      /* Current X screen number. */
142    const char *fileName;               /* Path to servers file. */
143
144    serverPid = getpid();
145    screenNum = 0;
146    maxCards = 1;
147    fileName = SERVERSFILE;
148    debug = FALSE;
149
150    strcpy(display, ":0.0");
151    InitLog();
152    InitServerTable();
153
154    /* Process command line switches. */
155    for (;;) {
156        int c;
157        int option_index = 0;
158        struct option long_options[] = {
159            // name, has_arg, flag, val
160            { 0,0,0,0 },
161        };
162
163        c = getopt_long(argc, argv, "x:f:d", long_options, &option_index);
164        if (c == -1) {
165            break;
166        }
167
168        switch(c) {
169        case 'x':                       /* Number of video cards */
170            maxCards = strtoul(optarg, 0, 0);
171            if ((maxCards < 1) || (maxCards > 10)) {
172                fprintf(stderr, "Bad number of max videocards specified\n");
173                return 1;
174            }
175            break;
176        case 'd':                       /* Debug  */
177            debug = TRUE;
178            break;
179
180        case 'f':                       /* Server file path. */
181            fileName = strdup(optarg);
182            break;
183
184        default:
185            fprintf(stderr,"Don't know what option '%c'.\n", c);
186            Help(argv[0]);
187            exit(1);
188        }
189    }
190
191    if (!debug) {
192        /* Detach this process from the controlling terminal process. The
193         * current directory becomes /tmp and redirect stdin/stdout/stderr to
194         * /dev/null. */
195        if (daemon(0,0) < 0) {
196            ERROR("Can't daemonize nanoscale: %s", strerror(errno));
197            exit(1);
198        }
199    }
200    serverPid = getpid();
201    if (!ParseServersFile(fileName, &numServers, serverTable)) {
202        ERROR("Exiting on invalid configuration");
203        exit(1);
204    }
205
206    /* Create listening sockets */
207    for (si = 0; si < numServers; si++) {
208        int fd, bool;
209        struct sockaddr_in addr;
210        RenderServer *serverPtr;
211
212        serverPtr = serverTable[si];
213
214        /* Create a socket for listening. */
215        fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
216        if (fd < 0) {
217            ERROR("can't create listener socket for \"%s\": %s",
218                  serverPtr->name, strerror(errno));
219            exit(1);
220        }
221        /* If the render server instance should be killed, drop the socket
222           address reservation immediately, don't linger. */
223        bool = TRUE;
224        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &bool, sizeof(bool)) < 0) {
225            ERROR("can't create set socket option for \"%s\": %s",
226                  serverPtr->name, strerror(errno));
227            exit(1);
228        }
229        /* Bind this address to the socket. */
230        addr.sin_family = AF_INET;
231        addr.sin_port = htons(serverPtr->port);
232        addr.sin_addr.s_addr = htonl(INADDR_ANY);
233        if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
234            ERROR("can't bind to socket for \"%s\": %s",
235                  serverPtr->name, strerror(errno));
236            exit(1);
237        }
238        /* Listen on the specified port. */
239        if (listen(fd, 5) < 0) {
240            ERROR("can't listen to socket for \"%s\": %s",
241                  serverPtr->name, strerror(errno));
242            exit(1);
243        }
244        serverPtr->listenerFd = fd;
245        INFO("%s listening on %d", serverPtr->name, serverPtr->port);
246    }
247
248    if (numServers == 0) {
249        ERROR("No servers designated.");
250        exit(1);
251    }
252    signal(SIGPIPE, SIG_IGN);
253#ifdef SA_NOCLDWAIT
254    memset(&action, 0, sizeof(action));
255    action.sa_flags = SA_NOCLDWAIT;
256    sigaction(SIGCHLD, &action, 0);
257#else
258    signal(SIGCHLD, SIG_IGN);
259#endif
260
261    /* Build the array of servers listener file descriptors. */
262    FD_ZERO(&serverFds);
263    maxFd = -1;
264    for (si = 0; si < numServers; si++) {
265        RenderServer *serverPtr;
266
267        serverPtr = serverTable[si];
268
269        FD_SET(serverPtr->listenerFd, &serverFds);
270        if (serverPtr->listenerFd > maxFd) {
271            maxFd = serverPtr->listenerFd;
272        }
273    }
274
275    for (;;) {
276        fd_set readFds;
277
278        /* Reset using the array of server file descriptors. */
279        memcpy(&readFds, &serverFds, sizeof(serverFds));
280        if (select(maxFd+1, &readFds, NULL, NULL, 0) <= 0) {
281            ERROR("Select failed: %s", strerror(errno));
282            break;                      /* Error on select. */
283        }
284        for (si = 0; si < numServers; si++) {
285            RenderServer *serverPtr;
286            pid_t child;
287            int sock;
288            socklen_t length;
289            struct sockaddr_in newaddr;
290
291            serverPtr = serverTable[si];
292            if (!FD_ISSET(serverPtr->listenerFd, &readFds)) {
293                continue;
294            }
295            /* Rotate the display's screen number.  If we have multiple video
296             * cards, try to spread the jobs out among them.  */
297            screenNum++;
298            if (screenNum >= maxCards) {
299                screenNum = 0;
300            }
301            /* Accept the new connection. */
302            length = sizeof(newaddr);
303#ifdef HAVE_ACCEPT4
304            sock = accept4(serverPtr->listenerFd, (struct sockaddr *)&newaddr,
305                           &length, SOCK_CLOEXEC);
306#else
307            sock = accept(serverPtr->listenerFd, (struct sockaddr *)&newaddr,
308                          &length);
309#endif
310            if (sock < 0) {
311                ERROR("Can't accept server \"%s\": %s", serverPtr->name,
312                      strerror(errno));
313                exit(1);
314            }
315#ifndef HAVE_ACCEPT4
316            int flags = fcntl(sock, F_GETFD);
317            flags |= FD_CLOEXEC;
318            if (fcntl(sock, F_SETFD, flags) < 0) {
319                ERROR("Can't set FD_CLOEXEC on socket \"%s\": %s",
320                        serverPtr->name, strerror(errno));
321                exit(1);
322            }
323#endif
324            INFO("Connecting \"%s\" to %s\n", serverPtr->name,
325                 inet_ntoa(newaddr.sin_addr));
326
327            /* Fork the new process.  Connect I/O to the new socket. */
328            child = fork();
329            if (child < 0) {
330                ERROR("Can't fork \"%s\": %s", serverPtr->name,
331                      strerror(errno));
332                continue;
333            }
334            if (child == 0) {           /* Child process. */
335                int i;
336
337                if ((!debug) && (setsid() < 0)) {
338                    ERROR("Can't setsid \"%s\": %s", serverPtr->name,
339                          strerror(errno));
340                    exit(1);
341                }
342                if ((!debug) && ((chdir("/")) < 0)) {
343                    ERROR("Can't change to root directory for \"%s\": %s",
344                          serverPtr->name, strerror(errno));
345                    exit(1);
346                }
347                if (serverPtr->combineLogs) {
348                    char path[BUFSIZ];
349                    int newFd;
350
351                    sprintf(path, "%s/%s-%d.log", LOGDIR,
352                        serverPtr->name, getpid());
353                    if (serverPtr->logStdout) {
354                        newFd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
355                    } else {
356                        newFd = open("/dev/null", O_WRONLY, 0600);
357                    }
358                    if (newFd < 0) {
359                        ERROR("%s: can't open \"%s\": %s", serverPtr->name,
360                              path, strerror(errno));
361                        exit(1);
362                    }
363                    if (dup2(newFd, 1) < 0) {
364                        ERROR("%s: can't dup stdout to \"%s\": %s",
365                              serverPtr->name, path, strerror(errno));
366                        exit(1);
367                    }
368                    if (dup2(newFd, 2) < 0) {
369                        ERROR("%s: can't dup stderr to \"%s\": %s",
370                              serverPtr->name, path, strerror(errno));
371                        exit(1);
372                    }
373                } else {
374                    char path[BUFSIZ];
375                    int newFd;
376
377                    sprintf(path, "%s/%s-%d.stdout", LOGDIR,
378                        serverPtr->name, getpid());
379                    if (serverPtr->logStdout) {
380                        newFd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
381                    } else {
382                        newFd = open("/dev/null", O_WRONLY, 0600);
383                    }
384                    if (newFd < 0) {
385                        ERROR("%s: can't open \"%s\": %s", serverPtr->name,
386                              path, strerror(errno));
387                        exit(1);
388                    }
389                    if (dup2(newFd, 1) < 0) {
390                        ERROR("%s: can't dup stdout to \"%s\": %s",
391                              serverPtr->name, path, strerror(errno));
392                        exit(1);
393                    }
394                    sprintf(path, "%s/%s-%d.stderr", LOGDIR,
395                        serverPtr->name, getpid());
396                    if (serverPtr->logStderr) {
397                        newFd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
398                    } else {
399                        newFd = open("/dev/null", O_WRONLY, 0600);
400                    }
401                    if (newFd < 0) {
402                        ERROR("%s: can't open \"%s\": %s", serverPtr->name,
403                              path, strerror(errno));
404                        exit(1);
405                    }
406                    if (dup2(newFd, 2) < 0) {
407                        ERROR("%s: can't dup stderr to \"%s\": %s",
408                              serverPtr->name, path, strerror(errno));
409                        exit(1);
410                    }
411                }
412                /* Make sure to close syslog in case it is using one of
413                 * the file descriptors we are about to close
414                 */
415                closelog();
416                /* Dup the socket to descriptors, e.g. 3 and 4 */
417                if (dup2(sock, serverPtr->inputFd) < 0)  { /* input */
418                    ERROR("%s: can't dup socket to fd %d: %s",
419                          serverPtr->name, serverPtr->inputFd,
420                          strerror(errno));
421                    exit(1);
422                }
423                /* If using a single descriptor for input and output, we don't
424                 * need to call dup2 again
425                 */
426                if (serverPtr->outputFd != serverPtr->inputFd) {
427                    if (dup2(sock, serverPtr->outputFd) < 0) { /* output */
428                        ERROR("%s: can't dup socket to fd %d: %s",
429                              serverPtr->name, serverPtr->outputFd,
430                              strerror(errno));
431                        exit(1);
432                    }
433                }
434                /* Close all the other descriptors. */
435                for (i = 3; i <= FD_SETSIZE; i++) {
436                    if (i != serverPtr->inputFd &&
437                        i != serverPtr->outputFd) {
438                        close(i);
439                    }
440                }
441
442                /* Set the screen number in the DISPLAY variable. */
443                display[3] = screenNum + '0';
444                setenv("DISPLAY", display, 1);
445                /* Don't pollute child's environment with our library path.
446                 * Library path should be explicitly set in config if needed.
447                 */
448                unsetenv("LD_LIBRARY_PATH");
449                /* Set the configured environment */
450                for (i = 0; i < serverPtr->numEnvArgs; i += 2) {
451                    INFO("Env: %s=%s", serverPtr->envArgs[i],
452                         serverPtr->envArgs[i+1]);
453                    setenv(serverPtr->envArgs[i], serverPtr->envArgs[i+1], 1);
454                }
455                {
456                    char *cmd;
457
458                    cmd = MergeArgs(serverPtr->numCmdArgs, serverPtr->cmdArgs);
459                    INFO("Executing %s: client=%s, \"%s\" in=%d out=%d on DISPLAY=%s",
460                         serverPtr->name, inet_ntoa(newaddr.sin_addr),
461                         cmd, serverPtr->inputFd, serverPtr->outputFd, display);
462                    FreeArgs(cmd);
463                    //closelog();
464                    /* Replace the current process with the render server. */
465                    execvp(serverPtr->cmdArgs[0], serverPtr->cmdArgs);
466                    ERROR("Can't execute \"%s\": %s", serverPtr->cmdArgs[0],
467                          strerror(errno));
468                    exit(1);
469                }
470            } else {
471                close(sock);
472            }
473        }
474    }
475    exit(1);
476}
Note: See TracBrowser for help on using the repository browser.