4 * @author Sebastian Hack
8 * Copyright (C) 2005 Universitaet Karlsruhe
9 * Released under the GPL
13 #error Sorry, lpp_server is for UNIX only.
16 #include <sys/types.h>
17 #include <sys/socket.h>
19 #include <sys/resource.h>
24 #include <netinet/in.h>
47 #include "lpp_cplex.h"
51 /* stack size of the solver thread. */
52 static size_t solver_stack_size = PTHREAD_STACK_MIN;
54 /* master listening socket. */
57 /* master semaphore */
60 /* title of the current process */
62 static int title_length;
64 static int n_children = 0;
66 extern char** environ;
68 typedef void (solver_func_t)(lpp_t *lpp);
70 typedef struct _job_t {
71 struct list_head list;
77 solver_func_t *solver_func;
82 #define set_solver_stack_size(size) solver_stack_size = MAX(PTHREAD_STACK_MIN, (size))
84 #define setproctitle(name, args...) snprintf(title,title_length,(name),##args)
86 static void initproctitle(int argc, char **argv) {
88 char **envp = environ;
91 * Move the environment so we can reuse the memory.
92 * (Code borrowed from sendmail.)
93 * WARNING: ugly assumptions on memory layout here;
94 * if this ever causes problems, #undef DO_PS_FIDDLING
96 for (i = 0; envp[i] != NULL; i++)
98 environ = (char **) malloc(sizeof(char *) * (i + 1));
101 for (i = 0; envp[i] != NULL; i++)
102 if ((environ[i] = strdup(envp[i])) == NULL)
108 title_length = envp[i-1] + strlen(envp[i-1]) - argv[0];
110 title_length = argv[argc-1] + strlen(argv[argc-1]) - argv[0];
113 memset(title, 0, title_length);
117 static void job_init(job_t *job, lpp_comm_t *comm, lpp_t *lpp, solver_func_t *solver_func)
120 memset(job, 0, sizeof(job[0]));
122 job->solver_func = solver_func;
124 job->csock = lpp_comm_fileno(comm);
125 job->received = time(NULL);
126 job->session = pthread_self();
130 static firm_dbg_module_t *dbg = NULL;
135 static int passive_tcp(uint16_t port, int queue_len)
139 struct protoent *ppe;
140 struct sockaddr_in sin;
142 memset(&sin, 0, sizeof(sin));
143 sin.sin_family = AF_INET;
144 sin.sin_addr.s_addr = INADDR_ANY;
145 sin.sin_port = htons(port);
147 ppe = getprotobyname("tcp");
148 s = socket(PF_INET, SOCK_STREAM, ppe->p_proto);
149 setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
151 if(bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
152 perror("bind server socket");
156 if(listen(s, queue_len) < 0) {
157 perror("listen server socket");
164 static void dummy_solver(lpp_t *lpp)
168 for(i = 0; i < lpp->var_next; ++i) {
169 lpp->vars[i]->value = i;
170 lpp->vars[i]->value_kind = lpp_value_solution;
174 fprintf(lpp->log, "dummy solver exiting now.\n");
179 lpp->sol_state = lpp_optimal;
182 static void segv_solver(lpp_t *lpp)
186 for(i = 0; i < lpp->var_next; ++i) {
187 lpp->vars[i]->value = i;
188 lpp->vars[i]->value_kind = lpp_value_solution;
192 fprintf(lpp->log, "segv dummy solver exiting now.\n");
198 #define DEFAULT_SOLVER lpp_solve_cplex
201 solver_func_t *solver;
205 { lpp_solve_cplex, "cplex", 1 },
206 { dummy_solver, "dummy", 2 },
207 { segv_solver, "segv", 2 },
211 static solver_func_t *find_solver(const char *name)
215 for(i = 0; i < sizeof(solvers) / sizeof(solvers[0]); ++i)
216 if(strcmp(solvers[i].name, name) == 0)
217 return solvers[i].solver;
222 static void *solver_thread(void * data)
225 DBG((dbg, LEVEL_1, "starting solver thread pid %d tid %d\n", getpid(), pthread_self()));
226 setproctitle("lpp_server [problem solving: %s]", job->lpp->name);
228 /* I may be cancelled at every time. */
229 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
232 job->solver_func(job->lpp);
234 /* notify the session thread that we are finished. */
235 fclose(job->lpp->log);
240 static int solve(lpp_comm_t *comm, job_t *job)
248 int retval, fd_rd, fd_comm;
249 pthread_attr_t solver_thr_attr;
251 struct sembuf semops;
252 /* try to acquire a lock for the solver resource. */
255 semops.sem_flg = SEM_UNDO;
256 retval = semop(sem, &semops, 1);
258 perror("free semaphore");
260 DBG((dbg, LEVEL_1, "job %d: solving problem %s\n", job->id, job->lpp->name));
262 /* set the log file of the lpp to the pipe. */
264 DBG((dbg, LEVEL_4, "pipe read %d write %d\n", fds[0], fds[1]));
266 fd_comm = lpp_comm_fileno(comm);
268 rd = fdopen(fd_rd, "r");
269 log = fdopen(fds[1], "w");
271 lpp_set_log(job->lpp, log);
273 /* also set the stack size of the solver thread to a considerable amount */
274 pthread_attr_init(&solver_thr_attr);
275 pthread_attr_setstacksize(&solver_thr_attr, solver_stack_size);
276 pthread_create(&job->solver, &solver_thr_attr, solver_thread, job);
279 /* set select timeout to 10 seconds */
283 /* set the file descriptors. */
285 FD_SET(fd_rd, &rfds);
286 FD_SET(fd_comm, &rfds);
287 DBG((dbg, LEVEL_4, "select %d %d\n", fd_rd, fd_comm));
288 retval = select(MAX(fd_rd, fd_comm) + 1, &rfds, NULL, NULL, &tv);
289 DBG((dbg, LEVEL_4, "retval %d\n", retval));
291 /* an event on one of the descriptors arrived. */
293 /* A log message arrived. */
294 if(FD_ISSET(fd_rd, &rfds)) {
296 /* if there is nothing more to read, the child died; we go home now. */
300 if(fgets(buf, sizeof(buf), rd)) {
301 DBG((dbg, LEVEL_4, "receiving log message %s\n", buf));
302 /* send the message over the net. */
303 lpp_writel(comm, LPP_CMD_INFO);
304 lpp_writes(comm, buf);
309 /* A network message arrived. */
310 if(FD_ISSET(fd_comm, &rfds)) {
314 retval = read(fd_comm, &cmd, sizeof(cmd));
316 DBG((dbg, LEVEL_2, "cancelling solver thread tid %d\n", job->solver));
317 //pthread_cancel(job->solver);
324 /* eat senseless data. */
326 while(read(fd_comm, &cmd, sizeof(cmd)) > 0);
333 pthread_join(job->solver, NULL);
336 semops.sem_flg = SEM_UNDO;
337 retval = semop(sem, &semops, 1);
339 perror("free semaphore");
346 static void *session(int fd)
348 solver_func_t *solver = DEFAULT_SOLVER;
349 lpp_comm_t *comm = lpp_comm_new(fd, LPP_BUFSIZE);
351 DBG((dbg, LEVEL_1, "starting session thread pid %d tid %d\n", getpid(), pthread_self()));
352 setproctitle("lpp_server [child]");
354 /* install the signal handler which gets triggered when the child dies. */
355 DBG((dbg, LEVEL_1, "new thread\n"));
358 int cmd = lpp_readl(comm);
360 DBG((dbg, LEVEL_2, "command: %s(%d)\n", lpp_get_cmd_name(cmd), cmd));
361 setproctitle("lpp_server [command %s(%d)]", lpp_get_cmd_name(cmd), cmd);
363 /* we could not read from the socket, so the connection died. bail out. */
368 case LPP_CMD_PROBLEM:
373 lpp = lpp_deserialize(comm);
374 lpp_deserialize_values(comm, lpp, lpp_value_start);
375 DBG((dbg, LEVEL_3, "problem %s received\n", lpp->name));
376 job_init(&job, comm, lpp, solver);
378 DBG((dbg, LEVEL_3, "starting job for problem %s\n", lpp->name));
379 setproctitle("lpp_server [problem waiting: %s]", lpp->name);
382 lpp_writel(comm, LPP_CMD_SOLUTION);
383 lpp_serialize_stats(comm, lpp);
384 lpp_serialize_values(comm, lpp, lpp_value_solution);
386 DBG((dbg, LEVEL_1, "job %d: finished with problem %s\n", job.id, lpp->name));
387 setproctitle("lpp_server [problem finished: %s]", lpp->name);
395 lpp_readbuf(comm, buf, sizeof(buf));
396 solver = find_solver(buf);
397 DBG((dbg, LEVEL_2, "setting solver to: %s\n", buf));
398 //lpp_send_res(comm, solver != NULL, "could not find solver: %s", buf);
401 case LPP_CMD_SOLVERS:
403 int i, n = ARRAY_SIZE(solvers);
405 for(i = 0; i < n; ++i)
406 lpp_writes(comm, solvers[i].name);
411 case LPP_CMD_SET_DEBUG:
413 int mask = lpp_readl(comm);
414 firm_dbg_set_mask(dbg, mask);
422 fprintf(stderr, "illegal command %d. exiting\n", cmd);
428 /* signal the queue, bail out and we are free now. */
435 static void child_handler(int sig)
442 pid = waitpid(-1, &status, WNOHANG);
444 if(WIFEXITED(status)) {
445 DBG((dbg, LEVEL_1, "child %d died normally with return value %d\n", pid, WEXITSTATUS(status)));
448 setproctitle("lpp_server [main (%d %s)]", n_children, (n_children>1)?"children":"child");
450 setproctitle("lpp_server [main]");
451 } else if(WIFSIGNALED(status)) {
452 DBG((dbg, LEVEL_1, "child %d died by signal %d\n", pid, WTERMSIG(status)));
455 setproctitle("lpp_server [main (%d %s)]", n_children, (n_children>1)?"children":"child");
457 setproctitle("lpp_server [main]");
459 DBG((dbg, LEVEL_1, "child %d did something unexpected\n", pid));
461 pid = waitpid(-1, &status, WNOHANG);
465 static void main_loop(void)
469 DBG((dbg, LEVEL_1, "master pid %d\n", getpid()));
471 msock = passive_tcp(LPP_PORT, 10);
474 struct sockaddr_in fsin;
475 socklen_t len = sizeof(fsin);
478 csock = accept(msock, (struct sockaddr *) &fsin, &len);
483 fprintf(stderr, "could not accept: %s\n", strerror(errno));
488 case 0: /* we're in the new child, start the session handler */
492 case -1: /* error, die! */
495 default: /* if we're in the parent just continue */
497 setproctitle("lpp_server [main (%d %s)]", n_children, (n_children>1)?"children":"child");
504 static void toggle_dbg(int num)
509 firm_dbg_set_mask(dbg, mask);
512 #define SEMKEYPATH "/tmp/lppkey"
515 static void print_syntax(void) {
516 fprintf(stderr, "lpp_server [-g <dbg level>] [-s <thread stack size>]\n");
519 int main(int argc, char *argv[])
524 struct sigaction sigact1, sigact2;
526 dbg = firm_dbg_register("lpp.server");
527 firm_dbg_set_mask(dbg, 1);
529 set_solver_stack_size(128 * 1024 * 1024);
532 while((c = getopt(argc, argv, "s:g:")) != -1) {
535 set_solver_stack_size(atoi(optarg));
538 firm_dbg_set_mask(dbg, atoi(optarg));
546 initproctitle(argc, argv);
547 setproctitle("lpp_server [main]");
549 memset(&sigact1, 0, sizeof(sigact1));
550 sigact1.sa_handler = toggle_dbg;
551 sigaction(SIGUSR1, &sigact1, NULL);
553 memset(&sigact2, 0, sizeof(sigact2));
554 sigact2.sa_handler = child_handler;
555 sigact2.sa_flags = SA_NOCLDSTOP;
556 sigaction(SIGCHLD, &sigact2, NULL);
558 /* set up semaphore */
559 semkey = ftok(SEMKEYPATH,SEMKEYID);
560 if ( semkey == (key_t)-1 ) {
561 perror("ftok() for sem failed");
565 sem = semget( semkey, 1, 0666 | IPC_CREAT);// | IPC_EXCL );
567 perror("semget() failed");
571 ret = semctl(sem, 0, SETVAL, 1);
573 perror("semctl() failed");
579 ret = semctl( sem, 0, IPC_RMID );
581 printf("semctl() remove id failed\n");