/*************************************************************************** * Copyright (C) 2006 by couriousous, blino, trem * * couriousous@mandriva.org * * oblin@mandriva.com * * trem@mandriva.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "prcsys.h" #include "prcsys_files.h" #include "prcsys_list.h" #include "prcsys_action.h" #include "prcsys_log.h" /** * Run a command to start or stop a service. * Argument * format : format of the command to run * ... : all argument for the command * */ static void run_command(char * format, ...) { va_list list; char command[1024]; va_start(list, format); if (vsnprintf(command,sizeof(command), format, list) < sizeof(command)) { dbg("Running %s\n",command); if(test_mode) { puts(command); sleep(1); } else { system(command); } } else { printandlog("Can't run command %s: too long\n", va_arg(list, char*)); exit(1); /* panic. not very clean */ } va_end(list); } /* called every time a service is started/stopped * update the bootsplash progressbar */ void rc_splash(char * string) { char * env; int progress; char buf[255]; static pthread_mutex_t splash_mutex = PTHREAD_MUTEX_INITIALIZER; env = getenv("splash_rc"); if(!env || strcmp(env,"yes")) { return; } pthread_mutex_lock(&splash_mutex); if((env = getenv("progress")) == NULL) progress = 0; else progress = atoi(env); snprintf(buf,sizeof(buf),"%d",progress + 1); setenv("progress",buf,1); run_command("/sbin/splash.sh %s 2>&1",string); pthread_mutex_unlock(&splash_mutex); } /* this is the function which is threaded for every service */ /* it launch the service when his requires are fullfited */ void * service_thread(void * ptr) { struct service * serv = (struct service *) ptr; struct proreq *req; int file; FILE * f; int already_up = 0; char command[1024]; char tmpmask[] = "/tmp/init.XXXXXX"; char action[6]; dbg("Hi, I'm %s thread\n",serv->name); if(mode == MODE_START) { strcpy(action,"start"); for (req = serv->requires; req != NULL; req = req->next) { dbg("%s: Waiting on %s \n",serv->name,req->serv->name); pthread_mutex_lock(&req->serv->mut); /* no need to loop to recheck the condition after * the condition event, the value never do a started -> not started switch */ if(!req->serv->ready) pthread_cond_wait(&req->serv->cond,&req->serv->mut); pthread_mutex_unlock(&req->serv->mut); dbg("%s: Finished waiting on %s\n",serv->name,req->serv->name); } } else { /* MODE KILL */ /* We are in stop mode, so we must wait until the end of * every service who requires us to be able to stop us */ struct service * i; strcpy(action,"stop"); for(i = servlist; i != NULL; i = i->next) { if(requires_on_me(i,serv)) { dbg("%s: Waiting on %s \n",serv->name,i->name); pthread_mutex_lock(&i->mut); if(!i->ready) pthread_cond_wait(&i->cond,&i->mut); pthread_mutex_unlock(&i->mut); dbg("%s: Finished waiting on %s\n",serv->name,i->name); } } } /* All our requires are started * Let's go ! */ already_up = subsys_already_up(serv->name); if((!already_up && mode == MODE_START) || (already_up && mode == MODE_KILL)) { if(!serv->raw_console) { if((file = mkstemp(tmpmask)) == -1) { printandlog("Cannot create temporary file !\n"); strcpy(tmpmask,"/dev/null"); } else { close(file); } /* run our script ! */ run_command(FORMAT_CMD_OUTPUT, serv->file, action, tmpmask); } else { dbg("%s: Cannot log output, raw console access\n",serv->name); /* we want to be alone on the console */ dbg("%s: grabbing console mutex\n",serv->name); pthread_mutex_lock(&console_mutex); /* run our script ! */ run_command(FORMAT_CMD, serv->file, action); /* we let the console to others */ pthread_mutex_unlock(&console_mutex); dbg("%s: releasing console mutex\n",serv->name); } } /* tell other we are done */ pthread_mutex_lock(&serv->mut); serv->ready = 1; dbg("%s: Setting as ready\n",serv->name); pthread_cond_broadcast(&serv->cond); pthread_mutex_unlock(&serv->mut); dbg("%s: Calling rc_splash\n",serv->name); rc_splash(serv->name); if(((!already_up && mode == MODE_START) || (already_up && mode == MODE_KILL)) && !serv->raw_console) { size_t n; if((f = fopen(tmpmask,"r")) == NULL) { printandlog("Cannot open output file: %s\n",strerror(errno)); } else { /* print message on the console */ dbg("%s: Grabbing console mutex\n",serv->name); pthread_mutex_lock(&console_mutex); while((n = fread(command,1,sizeof(command),f)) != 0) { fwrite(command,1,n,stdout); writetolog(command,n); } pthread_mutex_unlock(&console_mutex); dbg("%s: releasing console mutex\n",serv->name); } fclose(f); } if(strcmp(tmpmask,"/dev/null")) unlink(tmpmask); return NULL; } static void system_go(char * directory) { struct service *i; pthread_t * thread_tab; int nbserv = 0; int p; nbserv = fill_requires(directory); malloc_or_die(thread_tab, nbserv * sizeof(pthread_t)); for(i = servlist, nbserv = 0; i != NULL; i = i->next, nbserv++) { pthread_create(&thread_tab[nbserv], NULL,service_thread,(void *) i); } /* wait for each service end */ for(p = 0;p < nbserv; p++) pthread_join(thread_tab[p],NULL); free(thread_tab); } /* start the system */ void mode_start(char * directory) { mode = MODE_START; system_go(directory); } /* stop de system */ void mode_kill(char * directory) { mode = MODE_KILL; system_go(directory); }