source: branches/blt4/packages/vizservers/nanoscale/server2.c @ 2340

Last change on this file since 2340 was 2340, checked in by gah, 13 years ago
File size: 8.6 KB
Line 
1
2#include <stdio.h>
3#include <unistd.h>
4#include <stdlib.h>
5#include <signal.h>
6#include <sys/types.h>
7#include <sys/socket.h>
8#include <sys/wait.h>
9#include <sys/select.h>
10#include <sys/time.h>
11#include <arpa/inet.h>
12#include <fcntl.h>
13#include <netinet/in.h>
14#include <getopt.h>
15#include <errno.h>
16#include <syslog.h>
17#include <stdarg.h>
18#include <tcl.h>
19
20#define TRUE    1
21#define FALSE   0
22#define DEF_SERVERS  "/opt/hubzero/rappture/render/lib/servers.cf"
23
24#define ERROR(...)      LogMessage(LOG_ERR, __FILE__, __LINE__, __VA_ARGS__)
25#ifdef WANT_TRACE
26#define TRACE(...)      LogMessage(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__)
27#else
28#define TRACE(...)
29#endif
30#define WARN(...)       LogMessage(LOG_WARNING, __FILE__, __LINE__, __VA_ARGS__)
31#define INFO(...)       LogMessage(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__)
32
33typedef struct {
34    const char *name;                   /* Name of server. */
35    int port;                           /* Port to listen to. */
36    int argc;                           /* # of args in command.  */
37    char **argv;                        /* Command to execute for server. */
38    int listenerFd;                     /* Descriptor the of listener socket. */
39} RenderServer;
40
41static Tcl_HashTable serverTable;
42static int debugFlag = FALSE;
43
44void
45LogMessage(int priority, const char *path, int lineNum, const char* fmt, ...)
46{
47#define MSG_LEN (2047)
48    char message[MSG_LEN+1];
49    const char *s;
50    int length;
51    va_list lst;
52
53    va_start(lst, fmt);
54    s = strrchr(path, '/');
55    if (s == NULL) {
56        s = path;
57    } else {
58        s++;
59    }
60    length = snprintf(message, MSG_LEN, "line %d of \"%s\": ", lineNum, s);
61    length += vsnprintf(message + length, MSG_LEN - length, fmt, lst);
62    message[MSG_LEN] = '\0';
63    if (debugFlag) {
64        fprintf(stderr, "%s\n", message);
65    } else {
66        syslog(priority, message, length);
67    }
68}
69
70static void
71Help(const char *program)
72{
73    fprintf(stderr,
74        "Syntax: %s [-d] [-f serversFile] [-x numVideoCards]\n", program);
75    exit(1);
76}
77
78static int
79RegisterServerCmd(ClientData clientData, Tcl_Interp *interp, int objc,
80                  Tcl_Obj *const *objv)
81{
82    Tcl_Obj *objPtr;
83    const char *serverName;
84    int bool, isNew;
85    int f;
86    int port;
87    struct sockaddr_in addr;
88    RenderServer *serverPtr;
89    Tcl_HashEntry *hPtr;
90
91    if (objc != 4) {
92        Tcl_AppendResult("wrong # args: should be \"", Tcl_GetString(objv[0]),
93                         " serverName port cmd", (char *)NULL);
94        return TCL_ERROR;
95    }
96    serverName = Tcl_GetString(objv[1]);
97    if (Tcl_GetInt(interp, objv[2], &port) != TCL_OK) {
98        return TCL_ERROR;
99    }
100    hPtr = Tcl_CreateHashEntry(&serverTable, (char *)port, &isNew);
101    if (!isNew) {
102        Tcl_AppendResult("a server is already listening on port ",
103                Tcl_GetString(objv[2]), (char *)NULL);
104        return TCL_ERROR;
105    }
106    objPtr = Tcl_SubstObj(interp, objv[3],
107                          TCL_SUBST_VARIABLES | TCL_SUBST_BACKSLASHES);
108    if (Tcl_SplitList(interp, Tcl_GetString(objPtr), &argc, &argv) != TCL_OK) {
109        return TCL_ERROR;
110    }
111
112    // Create a socket for listening.
113    f = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
114    if (f < 0) {
115        Tcl_AppendResult(interp, "can't create listerner socket for \"",
116                serverName, "\": ", Tcl_PosixError(interp), (char *)NULL);
117        return TCL_ERROR;
118    }
119 
120    // If program is killed, drop the socket address reservation immediately.
121    bool = TRUE;
122    status = setsockopt(f, SOL_SOCKET, SO_REUSEADDR, &bool, sizeof(bool));
123    if (status < 0) {
124        Tcl_AppendResult(interp, "can't create set socket option for \"",
125                serverName, "\": ", Tcl_PosixError(interp), (char *)NULL);
126        return TCL_ERROR;
127    }
128
129    /* Bind this address to the socket. */
130    addr.sin_family = AF_INET;
131    addr.sin_port = htons(port);
132    addr.sin_addr.s_addr = htonl(INADDR_ANY);
133    status = bind(f, (struct sockaddr *)&addr, sizeof(addr));
134    if (status < 0) {
135        Tcl_AppendResult(interp, "can't bind to socket for \"",
136                serverName, "\": ", Tcl_PosixError(interp), (char *)NULL);
137        return TCL_ERROR;
138    }
139    /* Listen on the specified port. */
140    status = listen(f, 5);
141    if (status < 0) {
142        Tcl_AppendResult(interp, "can't listen to socket for \"",
143                serverName, "\": ", Tcl_PosixError(interp), (char *)NULL);
144        return TCL_ERROR;
145    }
146    serverPtr = malloc(sizeof(RenderServer));
147    if (serverPtr == NULL) {
148        Tcl_AppendResult(interp, "can't allocate structure for \"",
149                serverName, "\": ", Tcl_PosixError(interp), (char *)NULL);
150        return TCL_ERROR;
151    }
152    serverPtr->name = strdup(serverName);
153    serverPtr->argv = argv;
154    serverPtr->argc = argc;
155    serverPtr->listenerFd = f;
156    return TCL_OK;
157}
158
159static int
160ParseServersFile(const char *fileName)
161{
162    Tcl_Interp *interp;
163
164    interp = Tcl_CreateInterp();
165    Tcl_MakeSafe(interp);
166    Tcl_CreateObjCommand(interp, "register_server", RegisterServerCmd, NULL,
167                         NULL);
168    if (Tcl_EvalFile(interp, fileName) != TCL_OK) {
169        ERROR("can't add server: %s", Tcl_GetString(Tcl_GetObjResult(interp)));
170        return FALSE;
171    }
172    Tcl_DeleteInterp(interp);
173    return TRUE;
174}
175
176int
177main(int argc, char **argv)
178{
179    int status;
180    struct sockaddr_in recvAddr;
181    int serverFds[FD_SETSIZE];
182    int n;
183    char displaySetting[200];
184    int maxCards;
185    int dispNum;
186    Tcl_HashEntry *hPtr;
187    Tcl_HashSearch iter;
188    const char *fileName;
189
190    dispNum = 0;
191    maxCards = 1;
192    fileName = DEF_SERVERS;
193    debugFlags = FALSE;
194    strcpy(displaySetting, "DISPLAY=:0.0");
195    if (putenv(displaySetting) < 0) {
196        ERROR("can't set DISPLAY variable: ", strerror(errno));
197        exit(1);
198    }
199    Tcl_InitHashTable(&serverTable, TCL_ONE_WORD_KEYS);
200    while (1) {
201        int c;
202        int option_index = 0;
203        struct option long_options[] = {
204            // name, has_arg, flag, val
205            { 0,0,0,0 },
206        };
207
208        c = getopt_long(argc, argv, "+p:x:d", long_options, &option_index);
209        if (c == -1) {
210            break;
211        }
212
213        switch(c) {
214        case 'x':                       /* Number of video cards */
215            maxCards = strtoul(optarg, 0, 0);
216            if ((maxCards < 1) || (maxCards > 10)) {
217                fprintf(stderr, "bad number of max videocards specified\n");
218                return 1;
219            }
220            break;
221        case 'd':                       /* Debug  */
222            debugFlag = 1;
223            break;
224
225        case 'f':                       /* Server files */
226            fileName = strdup(optarg);
227            break;
228
229        default:
230            fprintf(stderr,"Don't know what option '%c'.\n", c);
231            Help(argv[0]);
232            return 1;
233        }
234    }
235
236    if (!ParseServersFile(fileName)) {
237        exit(1);
238    }   
239
240    if (serverTable.numEntries == 0) {
241        ERROR("no servers designated.");
242        exit(0);
243    }
244
245
246    if (!debugFlag) {
247        if (daemon(0,1) < 0) {
248            ERROR("can't daemonize nanoscale: ", strerror(errno));
249            exit(1);
250        }
251    }
252
253    /* Build the array of servers listener file descriptors. */
254    FD_ZERO(&serverFds);
255    for (hPtr = Tcl_FirstHashEntry(&serverTable, &iter); hPtr != NULL;
256         hPtr = Tcl_NextHashEntry(&iter)) {
257        RenderServer *serverPtr;
258
259        serverPtr = Tcl_GetHashValue(hPtr);
260        FD_SET(serverPtr->listenerFd, &serverFds);
261        if (serverPtr->listenerFd > maxFd) {
262            maxFs = serverPtr->listenerFd;
263        }
264    }
265    while (select(maxFd+1, serverFds, NULL, NULL, 0) > 0) {
266
267        for (hPtr = Tcl_FirstHashEntry(&serverTable, &iter); hPtr != NULL;
268             hPtr = Tcl_NextHashEntry(&iter)) {
269            RenderServer *serverPtr;
270
271            serverPtr = Tcl_GetHashValue(hPtr);
272            if (FD_ISSET(serverPtr->listenerFd, &serverFds)) {
273                struct sockaddr_in newaddr;
274                unsigned int addrlen;
275                int f;
276                int child;
277
278                /* Accept the new connection. */
279                unsigned int addrlen = sizeof(newaddr);
280                f = accept(serverPtr->listenerFd, (struct sockaddr *)&newaddr,
281                           sizeof(newaddr));
282                if (f < 0) {
283                    ERROR("can't accept server \"", serverPtr->name,
284                          "\": ", strerror(errno));
285                    continue;
286                }
287                INFO("Connected to %s\n", inet_ntoa(newaddr.sin_addr));
288
289                dispNum++;
290                if (dispNum >= maxCards) {
291                    dispNum = 0;
292                }
293                /* Fork the new process.  Connect I/O to the new socket. */
294                child = fork();
295                if (child < 0) {
296                    ERROR("can't fork \"", serverPtr->name, "\": ",
297                        strerror(errno));
298                    continue;
299                } else if (child == 0) {
300                    int i;
301
302                    /* Child process. */
303                    if (!debugFlag) {
304                        /* Disassociate server from */
305                        status = daemon(0,1);
306                        if (status < 0) {
307                            ERROR("can't daemonize \"", serverPtr->name,
308                                  "\": ", strerror(errno));
309                        }
310                    }                       
311                    dup2(f, 0);         /* Stdin */
312                    dup2(f, 1);         /* Stdout */
313
314                    for(i = 3; i <= FD_SETSIZE; i++) {
315                        close(i);       /* Close all the other descriptors. */
316                    }
317                    if (maxCards > 1) {
318                        displaySetting[11] = dispNum + '0';
319                    }
320                    INFO("%s: client %s connecting to display %d", 
321                        serverPtr->name, inet_ntoa(newaddr.sin_addr), dispNum);
322                    status = execvp(serverPtr->argv[0], serverPtr->argv);
323                    ERROR("can't execute \"", serverPtr->argv[0],
324                          "\": ", strerror(errno));
325                    _exit(1);
326                }
327                _exit(EINVAL);
328            } else {
329                /* Reap initial child which will exit immediately
330                 * (grandchild continues) */
331                waitpid(status, NULL, 0);
332            }
333        }
334    }
335}
336
Note: See TracBrowser for help on using the repository browser.