/*************************************************************************** * 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" /* fill the "list" with what is found in the string "str" * Only fill the name and the flags. It does NOT resolve dependency */ /** * This function create a list with all name found in a string. * It does NOT resolve dependency * argument: * - list : pointer to the list * - str : string to parse * return: * - list is filled with name found in str * */ void fill_proreq_list(struct proreq ** list, char * str) { struct proreq * prt; int n; if(str == NULL) { *list = NULL; return; } /* remove useless character */ str += strspn(str," \t\n"); if(strlen(str) == 0) { *list = NULL; return; } while (*str != '\0') { malloc_or_die(prt,sizeof(struct proreq)); /* fill the list */ n = strcspn(str," \t\n"); if ( n > NAME_LENGTH ) n = NAME_LENGTH; strncpy(prt->name, str, n); prt->name[n] = '\0'; /* add this cell to the list */ prt->next = *list; *list = prt; /* go to next name */ str += n + strspn(str+n," \t\n"); } } /** * This function set buf to the name of the service provided by file path * This name is the name of the file of the link (if path is a link). For * example, if path == ../init.d/kheader then buf is set to 'kheader'. But * if the file isn't a link (that should not be possible), This name is the * name of the file without mode and order information. For example, if the * file == S95kheader, then buf is set to 'kheader'. * argument: * - buf : the buffer to set * - size : max size of the buffer * - path : the name of the file (with the path) * return: * - buf is set to the service name * */ void set_servname(char * buf, unsigned long size, char * path) { char buffer[1024]; int s; s = readlink(path, buffer, sizeof(buffer) - 1); if(s == -1 || s == 0) { printandlog("Cannot readlink %s\n", path); strncpy(buffer, path+3, sizeof(buffer)); } else buffer[s] = '\0'; strncpy(buf, basename(buffer), size); } #define INTERACTIVE_TAG "# X-Mandriva-Interactive" #define COMPAT_MODE_TAG "# X-Mandriva-Compat-Mode" /* fill the "list" service list with every service found in "dir" * the list is sorted */ void fill_service_list(struct service ** list, char * dir) { char ** filelist = list_files(dir); char ** temp; char buffer[1024]; for(temp = filelist; temp != NULL && *temp != NULL; temp++){ struct service * serv; malloc_or_die(serv,sizeof(struct service)); set_servname(serv->name, NAME_LENGTH, *temp); strncpy(serv->file, *temp, NAME_LENGTH); serv->requires = NULL; serv->provides = NULL; if(check_lsb_script(serv->file)) { if(test_mode) printandlog("%s is an LSB script \n",serv->file); serv->is_lsb = 1; serv->raw_console = check_prcsys_tag(serv->file, INTERACTIVE_TAG); /* some scripts are LSB-ified but should be run in "compat mode" ( = rc.local )*/ serv->compat_mode = check_prcsys_tag(serv->file, COMPAT_MODE_TAG); parse_file_provide_lsb(serv->file,buffer,sizeof(buffer)); fill_proreq_list(&serv->provides,buffer); parse_file_dep_lsb(serv->file,buffer,sizeof(buffer)); fill_proreq_list(&serv->requires,buffer); if(serv->provides == NULL) { struct proreq * prt; printandlog("WARNING ! %s doesn't provides anything, auto-adding his name\n",serv->file); malloc_or_die(prt,sizeof(struct proreq)); serv->provides = prt; prt->next = NULL; strncpy(prt->name, serv->name, NAME_LENGTH); } } else { struct proreq * prt; serv->is_lsb = 0; serv->compat_mode = 1; serv->raw_console = 1; malloc_or_die(prt,sizeof(struct proreq)); serv->provides = prt; prt->next = NULL; strncpy(prt->name,serv->name,NAME_LENGTH); } serv->ready = 0; pthread_mutex_init(&serv->mut,NULL); pthread_cond_init(&serv->cond,NULL); serv->next = *list; *list = serv; free(*temp); } free(filelist); } /* look up who provides "name" service * return the first service providing it */ struct service * who_provides(char * name) { struct service * i; for(i = servlist; i != NULL; i = i->next) { struct proreq * j; for(j = i->provides; j != NULL; j = j->next) { if(!strcasecmp(name, j->name)) return i; } } if(test_mode) printandlog("Requires %s NOT FOUND!\n",name); return NULL; } /* Check if the requires "j" of service "serv" is valid : * - Doesn't loop * This function return the pointer over the service who fullfit the requires "j" */ struct service * validate_requires(struct service * serv, struct proreq * j) { struct service * target; target = who_provides(j->name); if (target != NULL) { clean_graph(); run_graph(target); if(serv->count > 0) { printandlog("WARNING! Loop detected %s -> %s\n" " -> Ignoring requires %s\n", serv->name, target->name,target->name); return NULL; } } /* ok. no direct loop require OK */ return target; } /* return 1 if "serv" has a requires on "me", 0 otherwise */ int requires_on_me(struct service * serv, struct service * me) { struct proreq * i; for(i = serv->requires; i != NULL; i = i->next) { if(i->serv == me) return 1; } return 0; } void clean_graph(void) { /* fill 0 in every node */ struct service *i; for (i = servlist; i != NULL; i = i->next) { i->count = 0; } } /* Thanks misc for the algorithm */ /* We walk on the graph recursively until * - We search the end of the graph * - We pass twice on a same node */ void run_graph(struct service * serv) { struct proreq * i; struct service * j; if(++serv->count == 1) { for(i = serv->requires; i != NULL; i = i->next) { if((*i->name == '\0') || ((j = who_provides(i->name)) == NULL)) continue; run_graph(j); } } } /* return the number of service loaded */ /* and populate the service list ( it solve every dependency * so, after calling fill_requries, the system state is OK */ int fill_requires(char * directory) { struct service *i; struct proreq *j; int nbserv = 0; fill_service_list(&servlist,directory); for(i = servlist; i != NULL; i = i->next) { if(i->compat_mode) { /* this is a non-parallelisable init script */ /* requires everything which is before in the list * and make requires everything which is after in the list * requires himself, so we keep the lsb standard compatibility */ /* For MODE_KILL we do exactly the oposite since the requires will * be inversed lated */ /* for now, we put only requries on name, and we don't "resolve" * our requries since it will be done after */ struct service *t = servlist; /* print a warning only for non-lsb script ( rc.local is a non-parallelisable * script, but we don't want to print a warning for it, so it is LSB-ified * but with the X-Mandriva-Compat-Mode tag ) */ if (!i->is_lsb) printandlog("Activating Compat-mode for non LSB-script %s\n",i->name); for(;t != i; t = t->next) { struct proreq * prt; malloc_or_die(prt,sizeof(struct proreq)); prt->serv = NULL; if(mode == MODE_START) { if(t->provides && t->provides->name) strcpy(prt->name, t->provides->name); else strcpy(prt->name,t->name); prt->next = i->requires; i->requires = prt; } else { if(i->provides && i->provides->name) strcpy(prt->name,i->provides->name); else strcpy(prt->name,i->name); prt->next = t->requires; t->requires = prt; } } /* put a requires on i in every service next in the list */ for(t = t->next; t != NULL; t = t->next) { struct proreq * prt; malloc_or_die(prt,sizeof(struct proreq)); prt->serv = NULL; if(mode == MODE_START) { if(i->provides && i->provides->name) strcpy(prt->name,i->provides->name); else strcpy(prt->name,i->name); prt->next = t->requires; t->requires = prt; } else { if(t->provides && t->provides->name) strcpy(prt->name,t->provides->name); else strcpy(prt->name,t->name); prt->next = i->requires; i->requires = prt; } } } } for(i = servlist; i != NULL; i = i->next) { int restart; struct proreq * p; nbserv++; do { restart = 0; for(p = j = i->requires; j != NULL; j = j->next) { j->serv = validate_requires(i,j); if(j->serv == NULL) { struct proreq * temp; /* remove it from the list */ if(i->requires == j) { /* if first item in the list */ i->requires = j->next; free(j); restart = 1; break; } else p->next = j->next; temp = j; j = p; free(temp); } else p = j; } } while(restart); } return nbserv; }