copy comp_dir_value
[libfirm] / support / lpp_server / lpp_server.c
1 /**
2  * @file   lpp_server.c
3  * @date   19.07.2005
4  * @author Sebastian Hack
5  *
6  * lpp_solving server.
7  *
8  * Copyright (C) 2005 Universitaet Karlsruhe
9  * Released under the GPL
10  */
11
12 #ifdef _WIN32
13 #error Sorry, lpp_server is for UNIX only.
14 #endif
15
16 #include <sys/types.h>
17 #include <sys/socket.h>
18 #include <sys/time.h>
19 #include <sys/resource.h>
20 #include <sys/wait.h>
21 #include <sys/ipc.h>
22 #include <sys/sem.h>
23
24 #include <netinet/in.h>
25 #include <netdb.h>
26
27 #include <unistd.h>
28 #include <pthread.h>
29
30 #include <time.h>
31 #include <time.h>
32 #include <limits.h>
33 #include <errno.h>
34 #include <signal.h>
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <stdarg.h>
39
40 #include "debug.h"
41 #include "list.h"
42 #include "irtools.h"
43 #include "util.h"
44
45 #include "lpp_t.h"
46 #include "lpp_comm.h"
47 #include "lpp_solvers.h"
48
49 #define MAX_JOBS 128
50
51 /* stack size of the solver thread. */
52 static size_t solver_stack_size = PTHREAD_STACK_MIN;
53
54 /* master listening socket. */
55 static int msock;
56
57 /* master semaphore */
58 static int sem;
59
60 /* title of the current process */
61 static char *title;
62 static int title_length;
63
64 static int n_children = 0;
65
66 extern char** environ;
67
68 typedef struct _job_t {
69         struct list_head list;
70         int id;
71         pthread_t solver;
72         pthread_t session;
73         lpp_comm_t *comm;
74         lpp_t *lpp;
75         lpp_solver_func_t *solver_func;
76         time_t received;
77         int csock;
78 } job_t;
79
80 #define set_solver_stack_size(size) solver_stack_size = MAX(PTHREAD_STACK_MIN, (size))
81
82 #define setproctitle(name, args...) snprintf(title,title_length,(name),##args)
83
84 static void initproctitle(int argc, char **argv) {
85         int i;
86         char **envp = environ;
87
88         /*
89          * Move the environment so we can reuse the memory.
90          * (Code borrowed from sendmail.)
91          * WARNING: ugly assumptions on memory layout here;
92          *          if this ever causes problems, #undef DO_PS_FIDDLING
93          */
94         for (i = 0; envp[i] != NULL; i++)
95                 continue;
96         environ = (char **) malloc(sizeof(char *) * (i + 1));
97         if (environ == NULL)
98                 return;
99         for (i = 0; envp[i] != NULL; i++)
100                 if ((environ[i] = strdup(envp[i])) == NULL)
101                         return;
102         environ[i] = NULL;
103
104         title = argv[0];
105         if (i > 0)
106                 title_length = envp[i-1] + strlen(envp[i-1]) - argv[0];
107         else
108                 title_length = argv[argc-1] + strlen(argv[argc-1]) - argv[0];
109
110         argv[1] = NULL;
111         memset(title, 0, title_length);
112         --title_length;
113 }
114
115 static void job_init(job_t *job, lpp_comm_t *comm, lpp_t *lpp, lpp_solver_func_t *solver_func)
116 {
117   /* TODO MAXJOBS */
118   memset(job, 0, sizeof(job[0]));
119   job->lpp         = lpp;
120   job->solver_func = solver_func;
121   job->comm        = comm;
122   job->csock       = lpp_comm_fileno(comm);
123   job->received    = time(NULL);
124   job->session     = pthread_self();
125   job->id          = getpid();
126 }
127
128 static firm_dbg_module_t *dbg = NULL;
129
130 /**
131  * Set up a socket.
132  */
133 static int passive_tcp(uint16_t port, int queue_len)
134 {
135         int s;
136         int one = 1;
137         struct protoent    *ppe;
138         struct sockaddr_in sin;
139
140         memset(&sin, 0, sizeof(sin));
141         sin.sin_family       = AF_INET;
142         sin.sin_addr.s_addr  = INADDR_ANY;
143         sin.sin_port         = htons(port);
144
145         ppe = getprotobyname("tcp");
146         s = socket(PF_INET, SOCK_STREAM, ppe->p_proto);
147         setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
148
149         if(bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
150                 perror("bind server socket");
151                 exit(1);
152         }
153
154         if(listen(s, queue_len) < 0) {
155                 perror("listen server socket");
156                 exit(1);
157         }
158
159         return s;
160 }
161
162 static void *solver_thread(void * data)
163 {
164         job_t *job = data;
165         DBG((dbg, LEVEL_1, "starting solver thread pid %d tid %d\n", getpid(), pthread_self()));
166         setproctitle("lpp_server [problem solving: %s]", job->lpp->name);
167
168         /* I may be cancelled at every time. */
169         pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
170
171         /* solve */
172         job->solver_func(job->lpp);
173
174         /* notify the session thread that we are finished. */
175         fclose(job->lpp->log);
176
177         return NULL;
178 }
179
180 static int solve(lpp_comm_t *comm, job_t *job)
181 {
182         char buf[1024];
183         struct timeval tv;
184         fd_set rfds;
185         int fds[2];
186         FILE *rd, *log;
187         int res = 0;
188         int retval, fd_rd, fd_comm;
189         pthread_attr_t solver_thr_attr;
190
191         struct sembuf semops;
192         /* try to acquire a lock for the solver resource. */
193         semops.sem_num = 0;
194         semops.sem_op = -1;
195         semops.sem_flg = SEM_UNDO;
196         retval = semop(sem, &semops, 1);
197         if(retval < 0) {
198                 perror("free semaphore");
199         }
200         DBG((dbg, LEVEL_1, "job %d: solving problem %s\n", job->id, job->lpp->name));
201
202         /* set the log file of the lpp to the pipe. */
203         pipe(fds);
204         DBG((dbg, LEVEL_4, "pipe read %d write %d\n", fds[0], fds[1]));
205
206         fd_comm = lpp_comm_fileno(comm);
207         fd_rd   = fds[0];
208         rd      = fdopen(fd_rd, "r");
209         log     = fdopen(fds[1], "w");
210
211         lpp_set_log(job->lpp, log);
212
213         /* also set the stack size of the solver thread to a considerable amount */
214         pthread_attr_init(&solver_thr_attr);
215         pthread_attr_setstacksize(&solver_thr_attr, solver_stack_size);
216         pthread_create(&job->solver, &solver_thr_attr, solver_thread, job);
217
218         while(1) {
219                 /* set select timeout to 10 seconds */
220                 tv.tv_sec  = 10;
221                 tv.tv_usec = 0;
222
223                 /* set the file descriptors. */
224                 FD_ZERO(&rfds);
225                 FD_SET(fd_rd, &rfds);
226                 FD_SET(fd_comm, &rfds);
227                 DBG((dbg, LEVEL_4, "select %d %d\n", fd_rd, fd_comm));
228                 retval = select(MAX(fd_rd, fd_comm) + 1, &rfds, NULL, NULL, &tv);
229                 DBG((dbg, LEVEL_4, "retval %d\n", retval));
230
231                 /* an event on one of the descriptors arrived. */
232                 if(retval > 0) {
233                         /* A log message arrived. */
234                         if(FD_ISSET(fd_rd, &rfds)) {
235
236                                 /* if there is nothing more to read, the child died; we go home now. */
237                                 if(feof(rd))
238                                         break;
239
240                                 if(fgets(buf, sizeof(buf), rd)) {
241                                         DBG((dbg, LEVEL_4, "receiving log message %s\n", buf));
242                                         /* send the message over the net. */
243                                         lpp_writel(comm, LPP_CMD_INFO);
244                                         lpp_writes(comm, buf);
245                                         lpp_flush(comm);
246                                 }
247                         }
248
249                         /* A network message arrived. */
250                         if(FD_ISSET(fd_comm, &rfds)) {
251                                 int cmd;
252
253                                 retval = read(fd_comm, &cmd, sizeof(cmd));
254                                 if(retval == 0) {
255                                         DBG((dbg, LEVEL_2, "cancelling solver thread tid %d\n", job->solver));
256                                         //pthread_cancel(job->solver);
257                                         exit(1);
258                                         //res = 1;
259                                         //break;
260                                 }
261
262                                 switch(cmd) {
263                                         /* eat senseless data. */
264                                         default:
265                                                 while(read(fd_comm, &cmd, sizeof(cmd)) > 0) {
266                                                 }
267                                 }
268                                 res = 1;
269                         }
270                 }
271         }
272
273         pthread_join(job->solver, NULL);
274         semops.sem_num = 0;
275         semops.sem_op = 1;
276         semops.sem_flg = SEM_UNDO;
277         retval = semop(sem, &semops, 1);
278         if(retval < 0) {
279                 perror("free semaphore");
280         }
281
282         fclose(rd);
283         return res;
284 }
285
286 static void *session(int fd)
287 {
288         lpp_solver_func_t *solver = lpp_find_solver("dummy");
289         lpp_comm_t *comm          = lpp_comm_new(fd, LPP_BUFSIZE);
290
291         DBG((dbg, LEVEL_1, "starting session thread pid %d tid %d\n", getpid(), pthread_self()));
292         setproctitle("lpp_server [child]");
293
294         /* install the signal handler which gets triggered when the child dies. */
295         DBG((dbg, LEVEL_1, "new thread\n"));
296         for(;;) {
297                 char buf[64];
298                 int cmd = lpp_readl(comm);
299
300                 DBG((dbg, LEVEL_2, "command: %s(%d)\n", lpp_get_cmd_name(cmd), cmd));
301                 setproctitle("lpp_server [command %s(%d)]", lpp_get_cmd_name(cmd), cmd);
302                 switch(cmd) {
303                         /* we could not read from the socket, so the connection died. bail out. */
304                         case -1:
305                         case LPP_CMD_BAD:
306                                 goto end;
307
308                         case LPP_CMD_PROBLEM:
309                                 {
310                                         job_t job;
311                                         lpp_t *lpp;
312
313                                         lpp = lpp_deserialize(comm);
314                                         lpp_deserialize_values(comm, lpp, lpp_value_start);
315                                         DBG((dbg, LEVEL_3, "problem %s received\n", lpp->name));
316                                         job_init(&job, comm, lpp, solver);
317
318                                         DBG((dbg, LEVEL_3, "starting job for problem %s\n", lpp->name));
319                                         setproctitle("lpp_server [problem waiting: %s]", lpp->name);
320
321                                         solve(comm, &job);
322                                         lpp_writel(comm, LPP_CMD_SOLUTION);
323                                         lpp_serialize_stats(comm, lpp);
324                                         lpp_serialize_values(comm, lpp, lpp_value_solution);
325                                         lpp_flush(comm);
326                                         DBG((dbg, LEVEL_1, "job %d: finished with problem %s\n", job.id, lpp->name));
327                                         setproctitle("lpp_server [problem finished: %s]", lpp->name);
328
329                                         free_lpp(lpp);
330                                 }
331
332                                 break;
333
334                         case LPP_CMD_SOLVER:
335                                 lpp_readbuf(comm, buf, sizeof(buf));
336                                 solver = lpp_find_solver(buf);
337                                 DBG((dbg, LEVEL_2, "setting solver to: %s\n", buf));
338                                 //lpp_send_res(comm, solver != NULL, "could not find solver: %s", buf);
339                                 break;
340
341                         case LPP_CMD_SOLVERS: {
342                                 int i;
343
344                                 for(i = 0; lpp_solvers[i].solver != NULL; i++) {
345                                 }
346                                 lpp_writel(comm, i);
347
348                                 for(i = 0; lpp_solvers[i].solver != NULL; i++)
349                                         lpp_writes(comm, lpp_solvers[i].name);
350                                 lpp_flush(comm);
351                                 break;
352                         }
353
354                         case LPP_CMD_SET_DEBUG:
355                                 {
356                                         int mask = lpp_readl(comm);
357                                         firm_dbg_set_mask(dbg, mask);
358                                 }
359                                 break;
360
361                         case LPP_CMD_BYE:
362                                 goto end;
363
364                         default:
365                                 fprintf(stderr, "illegal command %d. exiting\n", cmd);
366                                 goto end;
367                 }
368         }
369
370 end:
371         /* signal the queue, bail out and we are free now. */
372         lpp_comm_free(comm);
373         close(fd);
374
375         exit(0);
376 }
377
378 static void child_handler(int sig)
379 {
380         pid_t pid;
381         int status;
382
383         (void) sig;
384
385         pid = waitpid(-1, &status, WNOHANG);
386         do {
387                 if(WIFEXITED(status)) {
388                         DBG((dbg, LEVEL_1, "child %d died normally with return value %d\n", pid, WEXITSTATUS(status)));
389                         --n_children;
390                         if(n_children != 0)
391                                 setproctitle("lpp_server [main (%d %s)]", n_children, (n_children>1)?"children":"child");
392                         else
393                                 setproctitle("lpp_server [main]");
394                 } else if(WIFSIGNALED(status)) {
395                         DBG((dbg, LEVEL_1, "child %d died by signal %d\n", pid, WTERMSIG(status)));
396                         --n_children;
397                         if(n_children != 0)
398                                 setproctitle("lpp_server [main (%d %s)]", n_children, (n_children>1)?"children":"child");
399                         else
400                                 setproctitle("lpp_server [main]");
401                 } else
402                         DBG((dbg, LEVEL_1, "child %d did something unexpected\n", pid));
403
404                 pid = waitpid(-1, &status, WNOHANG);
405         } while(pid > 0);
406 }
407
408 static void main_loop(void)
409 {
410         int csock;
411
412         DBG((dbg, LEVEL_1, "master pid %d\n", getpid()));
413
414         msock = passive_tcp(LPP_PORT, 10);
415
416         for(;;) {
417                 struct sockaddr_in fsin;
418                 socklen_t len = sizeof(fsin);
419                 pid_t child;
420
421                 csock = accept(msock, (struct sockaddr *) &fsin, &len);
422                 if(csock < 0) {
423                         if(errno == EINTR)
424                                 continue;
425                         else
426                                 fprintf(stderr, "could not accept: %s\n", strerror(errno));
427                 }
428
429                 child = fork();
430                 switch(child) {
431                         case 0: /* we're in the new child, start the session handler */
432                                 close(msock);
433                                 session(csock);
434                                 break;
435                         case -1: /* error, die! */
436                                 perror("fork");
437                                 exit(1);
438                         default: /* if we're in the parent just continue */
439                                 ++n_children;
440                                 setproctitle("lpp_server [main (%d %s)]", n_children, (n_children>1)?"children":"child");
441                                 close(csock);
442                                 break;
443                 }
444         }
445 }
446
447 static void toggle_dbg(int num)
448 {
449         static int mask = 0;
450         (void) num;
451         mask = ~mask;
452         firm_dbg_set_mask(dbg, mask);
453 }
454
455 #define SEMKEYPATH "/tmp/lppkey"
456 #define SEMKEYID 42
457
458 static void print_syntax(void) {
459         fprintf(stderr, "lpp_server [-g <dbg level>] [-s <thread stack size>]\n");
460 }
461
462 int main(int argc, char *argv[])
463 {
464         key_t semkey;
465         int ret;
466         int c;
467         struct sigaction sigact1, sigact2;
468
469         dbg = firm_dbg_register("lpp.server");
470         firm_dbg_set_mask(dbg, 1);
471
472         set_solver_stack_size(128 * 1024 * 1024);
473
474         /* parse options. */
475         while((c = getopt(argc, argv, "s:g:")) != -1) {
476                 switch(c) {
477                         case 's':
478                                 set_solver_stack_size(atoi(optarg));
479                                 break;
480                         case 'g':
481                                 firm_dbg_set_mask(dbg, atoi(optarg));
482                                 break;
483                         default:
484                                 print_syntax();
485                                 exit(1);
486                 }
487         }
488
489         initproctitle(argc, argv);
490         setproctitle("lpp_server [main]");
491
492         memset(&sigact1, 0, sizeof(sigact1));
493         sigact1.sa_handler = toggle_dbg;
494         sigaction(SIGUSR1, &sigact1, NULL);
495
496         memset(&sigact2, 0, sizeof(sigact2));
497         sigact2.sa_handler = child_handler;
498         sigact2.sa_flags = SA_NOCLDSTOP;
499         sigaction(SIGCHLD, &sigact2, NULL);
500
501         /* set up semaphore */
502         semkey = ftok(SEMKEYPATH,SEMKEYID);
503         if ( semkey == (key_t)-1 ) {
504                 perror("ftok() for sem failed");
505                 return 1;
506         }
507
508         sem = semget( semkey, 1, 0666 | IPC_CREAT);// | IPC_EXCL );
509         if ( sem == -1 ) {
510                 perror("semget() failed");
511                 return -1;
512         }
513
514         ret = semctl(sem, 0, SETVAL, 1);
515         if ( ret < 0 ) {
516                 perror("semctl() failed");
517                 return -1;
518         }
519
520         main_loop();
521
522         ret = semctl( sem, 0, IPC_RMID );
523         if (ret < 0) {
524                 printf("semctl() remove id failed\n");
525                 return -1;
526         }
527         return 0;
528 }