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_solvers.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 struct _job_t {
69 struct list_head list;
75 lpp_solver_func_t *solver_func;
80 #define set_solver_stack_size(size) solver_stack_size = MAX(PTHREAD_STACK_MIN, (size))
82 #define setproctitle(name, args...) snprintf(title,title_length,(name),##args)
84 static void initproctitle(int argc, char **argv) {
86 char **envp = environ;
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
94 for (i = 0; envp[i] != NULL; i++)
96 environ = (char **) malloc(sizeof(char *) * (i + 1));
99 for (i = 0; envp[i] != NULL; i++)
100 if ((environ[i] = strdup(envp[i])) == NULL)
106 title_length = envp[i-1] + strlen(envp[i-1]) - argv[0];
108 title_length = argv[argc-1] + strlen(argv[argc-1]) - argv[0];
111 memset(title, 0, title_length);
115 static void job_init(job_t *job, lpp_comm_t *comm, lpp_t *lpp, lpp_solver_func_t *solver_func)
118 memset(job, 0, sizeof(job[0]));
120 job->solver_func = solver_func;
122 job->csock = lpp_comm_fileno(comm);
123 job->received = time(NULL);
124 job->session = pthread_self();
128 static firm_dbg_module_t *dbg = NULL;
133 static int passive_tcp(uint16_t port, int queue_len)
137 struct protoent *ppe;
138 struct sockaddr_in sin;
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);
145 ppe = getprotobyname("tcp");
146 s = socket(PF_INET, SOCK_STREAM, ppe->p_proto);
147 setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
149 if(bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
150 perror("bind server socket");
154 if(listen(s, queue_len) < 0) {
155 perror("listen server socket");
162 static void *solver_thread(void * 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);
168 /* I may be cancelled at every time. */
169 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
172 job->solver_func(job->lpp);
174 /* notify the session thread that we are finished. */
175 fclose(job->lpp->log);
180 static int solve(lpp_comm_t *comm, job_t *job)
188 int retval, fd_rd, fd_comm;
189 pthread_attr_t solver_thr_attr;
191 struct sembuf semops;
192 /* try to acquire a lock for the solver resource. */
195 semops.sem_flg = SEM_UNDO;
196 retval = semop(sem, &semops, 1);
198 perror("free semaphore");
200 DBG((dbg, LEVEL_1, "job %d: solving problem %s\n", job->id, job->lpp->name));
202 /* set the log file of the lpp to the pipe. */
204 DBG((dbg, LEVEL_4, "pipe read %d write %d\n", fds[0], fds[1]));
206 fd_comm = lpp_comm_fileno(comm);
208 rd = fdopen(fd_rd, "r");
209 log = fdopen(fds[1], "w");
211 lpp_set_log(job->lpp, log);
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);
219 /* set select timeout to 10 seconds */
223 /* set the file descriptors. */
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));
231 /* an event on one of the descriptors arrived. */
233 /* A log message arrived. */
234 if(FD_ISSET(fd_rd, &rfds)) {
236 /* if there is nothing more to read, the child died; we go home now. */
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);
249 /* A network message arrived. */
250 if(FD_ISSET(fd_comm, &rfds)) {
253 retval = read(fd_comm, &cmd, sizeof(cmd));
255 DBG((dbg, LEVEL_2, "cancelling solver thread tid %d\n", job->solver));
256 //pthread_cancel(job->solver);
263 /* eat senseless data. */
265 while(read(fd_comm, &cmd, sizeof(cmd)) > 0) {
273 pthread_join(job->solver, NULL);
276 semops.sem_flg = SEM_UNDO;
277 retval = semop(sem, &semops, 1);
279 perror("free semaphore");
286 static void *session(int fd)
288 lpp_solver_func_t *solver = lpp_find_solver("dummy");
289 lpp_comm_t *comm = lpp_comm_new(fd, LPP_BUFSIZE);
291 DBG((dbg, LEVEL_1, "starting session thread pid %d tid %d\n", getpid(), pthread_self()));
292 setproctitle("lpp_server [child]");
294 /* install the signal handler which gets triggered when the child dies. */
295 DBG((dbg, LEVEL_1, "new thread\n"));
298 int cmd = lpp_readl(comm);
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);
303 /* we could not read from the socket, so the connection died. bail out. */
308 case LPP_CMD_PROBLEM:
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);
318 DBG((dbg, LEVEL_3, "starting job for problem %s\n", lpp->name));
319 setproctitle("lpp_server [problem waiting: %s]", lpp->name);
322 lpp_writel(comm, LPP_CMD_SOLUTION);
323 lpp_serialize_stats(comm, lpp);
324 lpp_serialize_values(comm, lpp, lpp_value_solution);
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);
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);
341 case LPP_CMD_SOLVERS: {
344 for(i = 0; lpp_solvers[i].solver != NULL; i++) {
348 for(i = 0; lpp_solvers[i].solver != NULL; i++)
349 lpp_writes(comm, lpp_solvers[i].name);
354 case LPP_CMD_SET_DEBUG:
356 int mask = lpp_readl(comm);
357 firm_dbg_set_mask(dbg, mask);
365 fprintf(stderr, "illegal command %d. exiting\n", cmd);
371 /* signal the queue, bail out and we are free now. */
378 static void child_handler(int sig)
385 pid = waitpid(-1, &status, WNOHANG);
387 if(WIFEXITED(status)) {
388 DBG((dbg, LEVEL_1, "child %d died normally with return value %d\n", pid, WEXITSTATUS(status)));
391 setproctitle("lpp_server [main (%d %s)]", n_children, (n_children>1)?"children":"child");
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)));
398 setproctitle("lpp_server [main (%d %s)]", n_children, (n_children>1)?"children":"child");
400 setproctitle("lpp_server [main]");
402 DBG((dbg, LEVEL_1, "child %d did something unexpected\n", pid));
404 pid = waitpid(-1, &status, WNOHANG);
408 static void main_loop(void)
412 DBG((dbg, LEVEL_1, "master pid %d\n", getpid()));
414 msock = passive_tcp(LPP_PORT, 10);
417 struct sockaddr_in fsin;
418 socklen_t len = sizeof(fsin);
421 csock = accept(msock, (struct sockaddr *) &fsin, &len);
426 fprintf(stderr, "could not accept: %s\n", strerror(errno));
431 case 0: /* we're in the new child, start the session handler */
435 case -1: /* error, die! */
438 default: /* if we're in the parent just continue */
440 setproctitle("lpp_server [main (%d %s)]", n_children, (n_children>1)?"children":"child");
447 static void toggle_dbg(int num)
452 firm_dbg_set_mask(dbg, mask);
455 #define SEMKEYPATH "/tmp/lppkey"
458 static void print_syntax(void) {
459 fprintf(stderr, "lpp_server [-g <dbg level>] [-s <thread stack size>]\n");
462 int main(int argc, char *argv[])
467 struct sigaction sigact1, sigact2;
469 dbg = firm_dbg_register("lpp.server");
470 firm_dbg_set_mask(dbg, 1);
472 set_solver_stack_size(128 * 1024 * 1024);
475 while((c = getopt(argc, argv, "s:g:")) != -1) {
478 set_solver_stack_size(atoi(optarg));
481 firm_dbg_set_mask(dbg, atoi(optarg));
489 initproctitle(argc, argv);
490 setproctitle("lpp_server [main]");
492 memset(&sigact1, 0, sizeof(sigact1));
493 sigact1.sa_handler = toggle_dbg;
494 sigaction(SIGUSR1, &sigact1, NULL);
496 memset(&sigact2, 0, sizeof(sigact2));
497 sigact2.sa_handler = child_handler;
498 sigact2.sa_flags = SA_NOCLDSTOP;
499 sigaction(SIGCHLD, &sigact2, NULL);
501 /* set up semaphore */
502 semkey = ftok(SEMKEYPATH,SEMKEYID);
503 if ( semkey == (key_t)-1 ) {
504 perror("ftok() for sem failed");
508 sem = semget( semkey, 1, 0666 | IPC_CREAT);// | IPC_EXCL );
510 perror("semget() failed");
514 ret = semctl(sem, 0, SETVAL, 1);
516 perror("semctl() failed");
522 ret = semctl( sem, 0, IPC_RMID );
524 printf("semctl() remove id failed\n");