source: nanoscale/branches/1.0/server.c

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

Merge selected nanoscale changes from trunk (by hand)

File size: 23.8 KB
RevLine 
[4132]1/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/* ======================================================================
[6623]3 *  Copyright (c) 2004-2016  HUBzero Foundation, LLC
[4132]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 */
[2430]9#define _GNU_SOURCE
10#include <sys/socket.h>
11
[2342]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>
[2368]20#include <sys/stat.h>
21#include <sys/file.h>
[2342]22#include <syslog.h>
23#include <unistd.h>
[2449]24#include <fcntl.h>
[2342]25
26#include <tcl.h>
27
[2449]28#include "config.h"
29
[4130]30#define TRUE    1
31#define FALSE   0
[2342]32
[2347]33#ifndef SERVERSFILE
34#define SERVERSFILE  "/opt/hubzero/rappture/render/lib/renderservers.tcl"
35#endif
36
[4108]37#ifndef LOGDIR
[4130]38#define LOGDIR          "/tmp"
[4108]39#endif
40
[2655]41#define ERROR(...)      SysLog(LOG_ERR, __FILE__, __LINE__, __VA_ARGS__)
42#define WARN(...)       SysLog(LOG_WARNING, __FILE__, __LINE__, __VA_ARGS__)
43#define INFO(...)       SysLog(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__)
[6623]44#ifdef WANT_TRACE
45#define TRACE(...)      SysLog(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__)
46#else
47#define TRACE(...)
48#endif
[2342]49
50static const char *syslogLevels[] = {
[4130]51    "emergency",                        /* System is unusable */
52    "alert",                            /* Action must be taken immediately */
53    "critical",                         /* Critical conditions */
54    "error",                            /* Error conditions */
55    "warning",                          /* Warning conditions */
56    "notice",                           /* Normal but significant condition */
57    "info",                             /* Informational */
58    "debug",                            /* Debug-level messages */
[2342]59};
60
61/* RenderServer --
62 *
[4130]63 *      Contains information to describe/execute a render server.
[2342]64 */
65typedef struct {
[4130]66    const char *name;                   /* Name of server. */
67    int port;                           /* Port to listen to. */
68    int numCmdArgs;                     /* # of args in command.  */
69    int numEnvArgs;                     /* # of args in environment.  */
70    char *const *cmdArgs;               /* Command to execute for
[4153]71                                         * server. */
[4130]72    char *const *envArgs;               /* Environment strings to set. */
73    int listenerFd;                     /* Descriptor of the listener
74                                           socket. */
75    int inputFd;                        /* Descriptor to dup input side of
76                                         * server socket. */
77    int outputFd;                       /* Descriptor to dup output side of
78                                         * server socket. */
79    int logStdout;                      /* Redirect server stdout to a
80                                           file. */
81    int logStderr;                      /* Redirect server stderr to a
82                                           file. */
83    int combineLogs;                    /* Combine server stdout/stderr in
84                                         * same file. */
[2342]85} RenderServer;
86
[4130]87static Tcl_HashTable serverTable;       /* Table of render servers
88                                         * representing services available to
89                                         * clients.  A new instance is forked
90                                         * and executed each time a new
91                                         * request is accepted. */
[2342]92static int debug = FALSE;
[2361]93static pid_t serverPid;
[2342]94
[2655]95static void
[6623]96Help(const char *program)
97{
98    fprintf(stderr,
99        "Syntax: %s [-d] [-f serversFile] [-x numVideoCards]\n", program);
100    exit(1);
101}
102
103static void
104InitLog()
105{
106    openlog("nanoscale", LOG_CONS | LOG_PID, LOG_USER);
107}
108
109static void
[2655]110SysLog(int priority, const char *path, int lineNum, const char* fmt, ...)
[2342]111{
112#define MSG_LEN (2047)
113    char message[MSG_LEN+1];
114    const char *s;
115    int length;
116    va_list lst;
117
118    va_start(lst, fmt);
119    s = strrchr(path, '/');
120    if (s == NULL) {
121        s = path;
122    } else {
123        s++;
124    }
[6623]125    if (serverPid != getpid()) {
126        length = snprintf(message, MSG_LEN, "(%d) %s: %s:%d ",
127                          serverPid, syslogLevels[priority], s, lineNum);
128    } else {
129        length = snprintf(message, MSG_LEN, "%s: %s:%d ",
130                          syslogLevels[priority], s, lineNum);
131    }
[2342]132    length += vsnprintf(message + length, MSG_LEN - length, fmt, lst);
133    message[MSG_LEN] = '\0';
134    if (debug) {
[4114]135        fprintf(stderr, "%s\n", message);
[2342]136    } else {
[4114]137        syslog(priority, message, length);
[2342]138    }
139}
140
[4113]141static RenderServer *
142NewServer(Tcl_Interp *interp, const char *name)
143{
144    RenderServer *serverPtr;
145
146    serverPtr = malloc(sizeof(RenderServer));
147    memset(serverPtr, 0, sizeof(RenderServer));
148    if (serverPtr == NULL) {
[4114]149        Tcl_AppendResult(interp, "can't allocate structure for \"",
150                name, "\": ", Tcl_PosixError(interp), (char *)NULL);
151        return NULL;
[4113]152    }
153    serverPtr->name = strdup(name);
154    serverPtr->outputFd = STDOUT_FILENO;
155    serverPtr->inputFd = STDIN_FILENO;
156    serverPtr->combineLogs = TRUE;
157    serverPtr->logStdout = TRUE;
158    serverPtr->logStderr = TRUE;
159    return serverPtr;
160}
161
162static int
[4131]163ParseSwitches(Tcl_Interp *interp, RenderServer *serverPtr, int *objcPtr,
[4114]164              Tcl_Obj ***objvPtr)
[4113]165{
166    int i, objc;
167    Tcl_Obj **objv;
168
169    objc = *objcPtr;
170    objv = *objvPtr;
171    for (i = 3; i < objc; i += 2) {
[4114]172        const char *string;
173        char c;
[4113]174
[4114]175        string = Tcl_GetString(objv[i]);
176        if (string[0] != '-') {
177            break;
178        }
179        c = string[1];
180        if ((c == 'i') && (strcmp(string, "-input") == 0)) {
181            int f;
[4131]182
[4114]183            if (Tcl_GetIntFromObj(interp, objv[i+1], &f) != TCL_OK) {
184                return TCL_ERROR;
185            }
[4130]186            serverPtr->inputFd = f;
[4114]187        } else if ((c == 'o') && (strcmp(string, "-output") == 0)) {
188            int f;
[4131]189
[4114]190            if (Tcl_GetIntFromObj(interp, objv[i+1], &f) != TCL_OK) {
191                return TCL_ERROR;
192            }
[4130]193            serverPtr->outputFd = f;
[4114]194        } else if ((c == 'l') && (strcmp(string, "-logstdout") == 0)) {
195            int state;
[4131]196
[4114]197            if (Tcl_GetBooleanFromObj(interp, objv[i+1], &state) != TCL_OK) {
198                return TCL_ERROR;
199            }
200            serverPtr->logStdout = state;
201        } else if ((c == 'l') && (strcmp(string, "-logstderr") == 0)) {
202            int state;
[4113]203
[4114]204            if (Tcl_GetBooleanFromObj(interp, objv[i+1], &state) != TCL_OK) {
205                return TCL_ERROR;
206            }
207            serverPtr->logStderr = state;
208        } else if ((c == 'c') && (strcmp(string, "-combinelogs") == 0)) {
209            int state;
[4113]210
[4114]211            if (Tcl_GetBooleanFromObj(interp, objv[i+1], &state) != TCL_OK) {
212                return TCL_ERROR;
213            }
214            serverPtr->combineLogs = state;
215        } else {
216            Tcl_AppendResult(interp, "unknown switch \"", string, "\"",
217                             (char *)NULL);
218            return TCL_ERROR;
219        }
[4113]220    }
221    *objcPtr = objc - (i - 3);
222    *objvPtr = objv + (i - 3);
223    return TCL_OK;
224}
225
[2342]226/*
227 * RegisterServerCmd --
228 *
[4130]229 *      Registers a render server to be run when a client connects
230 *      on the designated port. The form of the commands is
[2342]231 *
232 *          register_server <name> <port> <cmd> <environ>
233 *
[4131]234 *      where
[2342]235 *
[4130]236 *          name        Token for the render server.
237 *          port        Port to listen to accept connections.
238 *          cmd         Command to be run to start the render server.
[4131]239 *          environ     Name-value pairs of representing environment
[4130]240 *                      variables.
[2342]241 *
[4131]242 *      Note that "cmd" and "environ" are variable and backslash
243 *      substituted.  A listener socket automatically is established on
244 *      the given port to accept client requests.
245 *
[4130]246 *      Example:
[2342]247 *
[4130]248 *          register_server myServer 12345 ?switches? {
249 *               /path/to/myserver arg arg
250 *          } {
251 *               LD_LIBRARY_PATH $libdir/myServer
[2342]252 *          }
253 *
254 */
255static int
256RegisterServerCmd(ClientData clientData, Tcl_Interp *interp, int objc,
[4114]257                  Tcl_Obj *const *objv)
[2342]258{
259    Tcl_Obj *objPtr;
260    const char *serverName;
261    int bool, isNew;
262    int f;
263    int port;
264    int numCmdArgs, numEnvArgs;
265    char *const *cmdArgs;
266    char *const *envArgs;
267    struct sockaddr_in addr;
268    RenderServer *serverPtr;
269    Tcl_HashEntry *hPtr;
270
[4113]271    if (objc < 4) {
[4131]272        Tcl_AppendResult(interp, "wrong # args: should be \"",
273                Tcl_GetString(objv[0]), " serverName port ?flags? cmd ?environ?",
[4114]274                (char *)NULL);
275        return TCL_ERROR;
[2342]276    }
277    serverName = Tcl_GetString(objv[1]);
278    if (Tcl_GetIntFromObj(interp, objv[2], &port) != TCL_OK) {
[4114]279        return TCL_ERROR;
[2342]280    }
281    hPtr = Tcl_CreateHashEntry(&serverTable, (char *)((long)port), &isNew);
282    if (!isNew) {
[4131]283        Tcl_AppendResult(interp, "a server is already listening on port ",
[4114]284                Tcl_GetString(objv[2]), (char *)NULL);
285        return TCL_ERROR;
[2342]286    }
[4113]287    serverPtr = NewServer(interp, serverName);
288    if (serverPtr == NULL) {
[4114]289        return TCL_ERROR;
[4113]290    }
291    Tcl_SetHashValue(hPtr, serverPtr);
292    if (ParseSwitches(interp, serverPtr, &objc, (Tcl_Obj ***)&objv) != TCL_OK) {
[4114]293        goto error;
[4113]294    }
[4131]295    objPtr = Tcl_SubstObj(interp, objv[3],
[4114]296                          TCL_SUBST_VARIABLES | TCL_SUBST_BACKSLASHES);
[4131]297    if (Tcl_SplitList(interp, Tcl_GetString(objPtr), &numCmdArgs,
[4114]298        (const char ***)&cmdArgs) != TCL_OK) {
299        goto error;
[2342]300    }
[4113]301    serverPtr->cmdArgs = cmdArgs;
302    serverPtr->numCmdArgs = numCmdArgs;
[4131]303
[4113]304    numEnvArgs = 0;
305    envArgs = NULL;
306    if (objc == 5) {
[4131]307        objPtr = Tcl_SubstObj(interp, objv[4],
[4114]308                TCL_SUBST_VARIABLES | TCL_SUBST_BACKSLASHES);
[4131]309        if (Tcl_SplitList(interp, Tcl_GetString(objPtr), &numEnvArgs,
[4114]310                (const char ***)&envArgs) != TCL_OK) {
311            goto error;
312        }
313        if (numEnvArgs & 0x1) {
[4131]314            Tcl_AppendResult(interp, "odd # elements in enviroment list",
[4114]315                             (char *)NULL);
316            goto error;
317        }
[4113]318    }
319    serverPtr->envArgs = envArgs;
320    serverPtr->numEnvArgs = numEnvArgs;
[2342]321
322    /* Create a socket for listening. */
323    f = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
324    if (f < 0) {
[6505]325        Tcl_AppendResult(interp, "can't create listener socket for \"",
[4114]326                serverPtr->name, "\": ", Tcl_PosixError(interp), (char *)NULL);
327        goto error;
[2342]328    }
[4131]329
[2342]330    /* If the render server instance should be killed, drop the socket address
331     * reservation immediately, don't linger. */
332    bool = TRUE;
333    if (setsockopt(f, SOL_SOCKET, SO_REUSEADDR, &bool, sizeof(bool)) < 0) {
[4114]334        Tcl_AppendResult(interp, "can't create set socket option for \"",
335                serverPtr->name, "\": ", Tcl_PosixError(interp), (char *)NULL);
336        goto error;
[2342]337    }
338
339    /* Bind this address to the socket. */
340    addr.sin_family = AF_INET;
341    addr.sin_port = htons(port);
342    addr.sin_addr.s_addr = htonl(INADDR_ANY);
343    if (bind(f, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
[4114]344        Tcl_AppendResult(interp, "can't bind to socket for \"",
345                serverPtr->name, "\": ", Tcl_PosixError(interp), (char *)NULL);
346        goto error;
[2342]347    }
348    /* Listen on the specified port. */
349    if (listen(f, 5) < 0) {
[4114]350        Tcl_AppendResult(interp, "can't listen to socket for \"",
351                serverPtr->name, "\": ", Tcl_PosixError(interp), (char *)NULL);
352        return TCL_ERROR;
[2342]353    }
354    serverPtr->listenerFd = f;
[4113]355
[2342]356    return TCL_OK;
[4113]357 error:
358    free(serverPtr);
359    return TCL_ERROR;
[2342]360}
361
[4131]362static int
[2342]363ParseServersFile(const char *fileName)
364{
365    Tcl_Interp *interp;
366
367    interp = Tcl_CreateInterp();
368    Tcl_MakeSafe(interp);
[4131]369    Tcl_CreateObjCommand(interp, "register_server", RegisterServerCmd, NULL,
[4114]370                         NULL);
[2342]371    if (Tcl_EvalFile(interp, fileName) != TCL_OK) {
[4114]372        ERROR("Can't add server: %s", Tcl_GetString(Tcl_GetObjResult(interp)));
373        return FALSE;
[2342]374    }
375    Tcl_DeleteInterp(interp);
376    return TRUE;
377}
378
[6623]379static void
380InitServerTable()
381{
382    Tcl_InitHashTable(&serverTable, TCL_ONE_WORD_KEYS);
383}
384
[4131]385int
[2342]386main(int argc, char **argv)
387{
[2377]388#ifdef SA_NOCLDWAIT
389    struct sigaction action;
390#endif
[2342]391    fd_set serverFds;
[4130]392    int maxFd;                          /* Highest file descriptor in use. */
[4131]393    char display[200];                  /* String used to manage the X
394                                         * DISPLAY variable for each render
[4130]395                                         * server instance. */
396    int maxCards;                       /* Maximum number of video cards, each
397                                         * represented by a different X
398                                         * screen.  */
399    int screenNum;                      /* Current X screen number. */
[2342]400    Tcl_HashEntry *hPtr;
401    Tcl_HashSearch iter;
[4130]402    const char *fileName;               /* Path to servers file. */
[4131]403
[2365]404    serverPid = getpid();
[2382]405    screenNum = 0;
[2342]406    maxCards = 1;
[2347]407    fileName = SERVERSFILE;
[2342]408    debug = FALSE;
[2363]409
[2382]410    strcpy(display, ":0.0");
[6623]411    InitLog();
412    InitServerTable();
[2342]413
414    /* Process command line switches. */
[2656]415    for (;;) {
[4114]416        int c;
417        int option_index = 0;
418        struct option long_options[] = {
419            // name, has_arg, flag, val
420            { 0,0,0,0 },
421        };
[2342]422
[4114]423        c = getopt_long(argc, argv, "x:f:d", long_options, &option_index);
424        if (c == -1) {
425            break;
426        }
[2342]427
[4114]428        switch(c) {
[4130]429        case 'x':                       /* Number of video cards */
[4114]430            maxCards = strtoul(optarg, 0, 0);
431            if ((maxCards < 1) || (maxCards > 10)) {
432                fprintf(stderr, "Bad number of max videocards specified\n");
433                return 1;
434            }
435            break;
[4130]436        case 'd':                       /* Debug  */
[4114]437            debug = TRUE;
438            break;
[2342]439
[4130]440        case 'f':                       /* Server file path. */
[4114]441            fileName = strdup(optarg);
442            break;
[2342]443
[4114]444        default:
445            fprintf(stderr,"Don't know what option '%c'.\n", c);
446            Help(argv[0]);
447            exit(1);
448        }
[2342]449    }
450
451    if (!debug) {
[4114]452        /* Detach this process from the controlling terminal process. The
453         * current directory becomes /tmp and redirect stdin/stdout/stderr to
454         * /dev/null. */
455        if (daemon(0,0) < 0) {
456            ERROR("Can't daemonize nanoscale: %s", strerror(errno));
457            exit(1);
458        }
[2342]459    }
[2362]460    serverPid = getpid();
[2363]461    if (!ParseServersFile(fileName)) {
[6623]462        ERROR("Exiting on invalid configuration");
[4114]463        exit(1);
[4131]464    }
[2342]465
[2363]466    if (serverTable.numEntries == 0) {
[4114]467        ERROR("No servers designated.");
468        exit(1);
[2363]469    }
[2384]470    signal(SIGPIPE, SIG_IGN);
[2377]471#ifdef SA_NOCLDWAIT
472    memset(&action, 0, sizeof(action));
473    action.sa_flags = SA_NOCLDWAIT;
474    sigaction(SIGCHLD, &action, 0);
475#else
476    signal(SIGCHLD, SIG_IGN);
477#endif
[2363]478
[2342]479    /* Build the array of servers listener file descriptors. */
480    FD_ZERO(&serverFds);
481    maxFd = -1;
482    for (hPtr = Tcl_FirstHashEntry(&serverTable, &iter); hPtr != NULL;
[4114]483         hPtr = Tcl_NextHashEntry(&iter)) {
484        RenderServer *serverPtr;
[4131]485
[4114]486        serverPtr = Tcl_GetHashValue(hPtr);
487        FD_SET(serverPtr->listenerFd, &serverFds);
488        if (serverPtr->listenerFd > maxFd) {
489            maxFd = serverPtr->listenerFd;
490        }
[2342]491    }
492
493    for (;;) {
[4114]494        fd_set readFds;
[2342]495
[4114]496        /* Reset using the array of server file descriptors. */
497        memcpy(&readFds, &serverFds, sizeof(serverFds));
498        if (select(maxFd+1, &readFds, NULL, NULL, 0) <= 0) {
499            ERROR("Select failed: %s", strerror(errno));
[4130]500            break;                      /* Error on select. */
[4114]501        }
502        for (hPtr = Tcl_FirstHashEntry(&serverTable, &iter); hPtr != NULL;
503             hPtr = Tcl_NextHashEntry(&iter)) {
504            RenderServer *serverPtr;
505            pid_t child;
506            int sock;
507            socklen_t length;
508            struct sockaddr_in newaddr;
[2342]509
[4114]510            serverPtr = Tcl_GetHashValue(hPtr);
511            if (!FD_ISSET(serverPtr->listenerFd, &readFds)) {
[4131]512                continue;
[4114]513            }
514            /* Rotate the display's screen number.  If we have multiple video
515             * cards, try to spread the jobs out among them.  */
516            screenNum++;
517            if (screenNum >= maxCards) {
518                screenNum = 0;
519            }
520            /* Accept the new connection. */
521            length = sizeof(newaddr);
[2449]522#ifdef HAVE_ACCEPT4
[4131]523            sock = accept4(serverPtr->listenerFd, (struct sockaddr *)&newaddr,
[4132]524                           &length, SOCK_CLOEXEC);
[2449]525#else
[4131]526            sock = accept(serverPtr->listenerFd, (struct sockaddr *)&newaddr,
[4132]527                          &length);
[2449]528#endif
[4114]529            if (sock < 0) {
[4131]530                ERROR("Can't accept server \"%s\": %s", serverPtr->name,
[4114]531                      strerror(errno));
532                exit(1);
533            }
[2449]534#ifndef HAVE_ACCEPT4
[4114]535            int flags = fcntl(sock, F_GETFD);
536            flags |= FD_CLOEXEC;
537            if (fcntl(sock, F_SETFD, flags) < 0) {
[4131]538                ERROR("Can't set FD_CLOEXEC on socket \"%s\": %s",
[4114]539                        serverPtr->name, strerror(errno));
540                exit(1);
541            }
[2449]542#endif
[4131]543            INFO("Connecting \"%s\" to %s\n", serverPtr->name,
[4114]544                 inet_ntoa(newaddr.sin_addr));
[2368]545
[4114]546            /* Fork the new process.  Connect I/O to the new socket. */
547            child = fork();
548            if (child < 0) {
[4131]549                ERROR("Can't fork \"%s\": %s", serverPtr->name,
[4114]550                      strerror(errno));
551                continue;
[4131]552            }
[4130]553            if (child == 0) {           /* Child process. */
[4114]554                int i;
[4108]555
[4114]556                if ((!debug) && (setsid() < 0)) {
[4131]557                    ERROR("Can't setsid \"%s\": %s", serverPtr->name,
[4114]558                          strerror(errno));
559                    exit(1);
560                }
561                if ((!debug) && ((chdir("/")) < 0)) {
[4131]562                    ERROR("Can't change to root directory for \"%s\": %s",
[4114]563                          serverPtr->name, strerror(errno));
564                    exit(1);
565                }
566                if (serverPtr->combineLogs) {
567                    char path[BUFSIZ];
568                    int newFd;
[2367]569
[4131]570                    sprintf(path, "%s/%s-%d.log", LOGDIR,
[4114]571                        serverPtr->name, getpid());
572                    if (serverPtr->logStdout) {
[6622]573                        newFd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
[4114]574                    } else {
575                        newFd = open("/dev/null", O_WRONLY, 0600);
576                    }
[4131]577                    if (newFd < 0) {
[4114]578                        ERROR("%s: can't open \"%s\": %s", serverPtr->name,
579                              path, strerror(errno));
580                        exit(1);
[4131]581                    }
582                    if (dup2(newFd, 1) < 0) {
583                        ERROR("%s: can't dup stdout to \"%s\": %s",
[4114]584                              serverPtr->name, path, strerror(errno));
585                        exit(1);
586                    }
587                    if (dup2(newFd, 2) < 0) {
[4131]588                        ERROR("%s: can't dup stderr to \"%s\": %s",
[4114]589                              serverPtr->name, path, strerror(errno));
590                        exit(1);
591                    }
592                } else {
593                    char path[BUFSIZ];
594                    int newFd;
[4113]595
[4131]596                    sprintf(path, "%s/%s-%d.stdout", LOGDIR,
[4114]597                        serverPtr->name, getpid());
598                    if (serverPtr->logStdout) {
599                        newFd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
600                    } else {
601                        newFd = open("/dev/null", O_WRONLY, 0600);
602                    }
[4131]603                    if (newFd < 0) {
[4114]604                        ERROR("%s: can't open \"%s\": %s", serverPtr->name,
605                              path, strerror(errno));
606                        exit(1);
[4131]607                    }
608                    if (dup2(newFd, 1) < 0) {
609                        ERROR("%s: can't dup stdout to \"%s\": %s",
[4114]610                              serverPtr->name, path, strerror(errno));
611                        exit(1);
612                    }
[4131]613                    sprintf(path, "%s/%s-%d.stderr", LOGDIR,
[4114]614                        serverPtr->name, getpid());
615                    if (serverPtr->logStderr) {
616                        newFd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
617                    } else {
618                        newFd = open("/dev/null", O_WRONLY, 0600);
619                    }
[4131]620                    if (newFd < 0) {
[4114]621                        ERROR("%s: can't open \"%s\": %s", serverPtr->name,
622                              path, strerror(errno));
623                        exit(1);
[4131]624                    }
[4153]625                    if (dup2(newFd, 2) < 0) {
[4131]626                        ERROR("%s: can't dup stderr to \"%s\": %s",
[4114]627                              serverPtr->name, path, strerror(errno));
628                        exit(1);
629                    }
[4131]630                }
[6505]631                /* Make sure to close syslog in case it is using one of
632                 * the file descriptors we are about to close
633                 */
634                closelog();
[4132]635                /* Dup the socket to descriptors, e.g. 3 and 4 */
636                if (dup2(sock, serverPtr->inputFd) < 0)  { /* input */
637                    ERROR("%s: can't dup socket to fd %d: %s",
638                          serverPtr->name, serverPtr->inputFd,
[4114]639                          strerror(errno));
640                    exit(1);
641                }
[4154]642                /* If using a single descriptor for input and output, we don't
643                 * need to call dup2 again
644                 */
[4132]645                if (serverPtr->outputFd != serverPtr->inputFd) {
646                    if (dup2(sock, serverPtr->outputFd) < 0) { /* output */
647                        ERROR("%s: can't dup socket to fd %d: %s",
648                              serverPtr->name, serverPtr->outputFd,
649                              strerror(errno));
650                        exit(1);
651                    }
652                }
[4161]653                /* Close all the other descriptors. */
654                for (i = 3; i <= FD_SETSIZE; i++) {
655                    if (i != serverPtr->inputFd &&
656                        i != serverPtr->outputFd) {
657                        close(i);
658                    }
[4114]659                }
[2369]660
[4114]661                /* Set the screen number in the DISPLAY variable. */
662                display[3] = screenNum + '0';
663                setenv("DISPLAY", display, 1);
[6622]664                /* Don't pollute child's environment with our library path.
665                 * Library path should be explicitly set in config if needed.
666                 */
667                unsetenv("LD_LIBRARY_PATH");
668                /* Set the configured environment */
[4114]669                for (i = 0; i < serverPtr->numEnvArgs; i += 2) {
[6623]670                    INFO("Env: %s=%s", serverPtr->envArgs[i],
671                         serverPtr->envArgs[i+1]);
[4114]672                    setenv(serverPtr->envArgs[i], serverPtr->envArgs[i+1], 1);
673                }
[6623]674                {
[4153]675                    char *cmd;
676
677                    cmd = Tcl_Merge(serverPtr->numCmdArgs,
678                                    (const char *const *)serverPtr->cmdArgs);
679                    INFO("Executing %s: client=%s, \"%s\" in=%d out=%d on DISPLAY=%s",
[4161]680                         serverPtr->name, inet_ntoa(newaddr.sin_addr),
681                         cmd, serverPtr->inputFd, serverPtr->outputFd, display);
[6623]682                    Tcl_Free(cmd);
[4153]683                    /* Replace the current process with the render server. */
684                    execvp(serverPtr->cmdArgs[0], serverPtr->cmdArgs);
[6623]685                    ERROR("Can't execute \"%s\": %s", serverPtr->cmdArgs[0],
686                          strerror(errno));
[4153]687                    exit(1);
688                }
[4114]689            } else {
690                close(sock);
691            }
[4131]692        }
[2342]693    }
694    exit(1);
695}
Note: See TracBrowser for help on using the repository browser.