Crossfire Client, Branch  R11627
misc.c
Go to the documentation of this file.
00001 const char * const rcsid_common_misc_c =
00002     "$Id: misc.c 11626 2009-04-04 12:41:46Z lalo $";
00003 /*
00004     Crossfire client, a client program for the crossfire program.
00005 
00006     Copyright (C) 2006,2001 Mark Wedel & Crossfire Development Team
00007 
00008     This program is free software; you can redistribute it and/or modify
00009     it under the terms of the GNU General Public License as published by
00010     the Free Software Foundation; either version 2 of the License, or
00011     (at your option) any later version.
00012 
00013     This program is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016     GNU General Public License for more details.
00017 
00018     You should have received a copy of the GNU General Public License
00019     along with this program; if not, write to the Free Software
00020     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00021 
00022     The author can be reached via e-mail to crossfire-devel@real-time.com
00023 */
00024 
00025 /*
00026  * static char *rcsid_misc_c =
00027  *   "$Id: misc.c 11626 2009-04-04 12:41:46Z lalo $";
00028  */
00029 
00030 
00031 /* Contains misc useful functions that may be useful to various parts
00032  * of code, but are not especially tied to it.
00033  */
00034 
00035 #include "client.h"
00036 #include <stdarg.h>
00037 #include <stdio.h>
00038 #include <errno.h>
00039 #include <sys/stat.h>
00040 #include <sys/types.h>
00041 #ifndef WIN32
00042 #include <sys/wait.h>
00043 #else
00044 #include <direct.h>
00045 #include <io.h>
00046 #endif
00047 
00048 /*
00049  * Verifies that the directory exists, creates it if necessary
00050  * Returns -1 on failure
00051  */
00052 
00053 int make_path_to_dir (char *directory)
00054 {
00055     char buf[MAX_BUF], *cp = buf;
00056     struct stat statbuf;
00057 
00058     if (!directory || !*directory)
00059         return -1;
00060     strcpy (buf, directory);
00061     while ((cp = strchr (cp + 1, (int) '/'))) {
00062         *cp = '\0';
00063         if (stat (buf, &statbuf) || !S_ISDIR (statbuf.st_mode)) {
00064 #ifdef WIN32
00065             if (mkdir (buf)) {
00066 #else
00067             if (mkdir (buf, 0777)) {
00068 #endif
00069                 perror ("Couldn't make path to file");
00070                 return -1;
00071             }
00072         } else
00073             *cp = '/';
00074     }
00075     /* Need to make the final component */
00076     if (stat (buf, &statbuf) || !S_ISDIR (statbuf.st_mode)) {
00077 #ifdef WIN32
00078     if (mkdir (buf)) {
00079 #else
00080         if (mkdir (buf, 0777)) {
00081 #endif
00082             perror ("Couldn't make path to file");
00083             return -1;
00084         }
00085     }
00086     return 0;
00087 }
00088 
00089 
00090 /*
00091  * If any directories in the given path doesn't exist, they are created.
00092  */
00093 
00094 int make_path_to_file (char *filename)
00095 {
00096     char buf[MAX_BUF], *cp = buf;
00097     struct stat statbuf;
00098 
00099     if (!filename || !*filename)
00100         return -1;
00101     strcpy (buf, filename);
00102     while ((cp = strchr (cp + 1, (int) '/'))) {
00103         *cp = '\0';
00104         if (stat (buf, &statbuf) || !S_ISDIR (statbuf.st_mode)) {
00105 #ifdef WIN32
00106             if (mkdir (buf)) {
00107                 LOG(LOG_ERROR, "misc.c::make_path_to_file",
00108                     "Couldn't make path to file: %s", strerror(errno));
00109 #else
00110             if (mkdir (buf, 0777)) {
00111                 perror ("Couldn't make path to file");
00112 #endif
00113                 return -1;
00114             }
00115         }
00116         *cp = '/';
00117     }
00118     return 0;
00119 }
00120 /*
00121  * A replacement of strdup(), since it's not defined at some
00122  * unix variants.
00123  */
00124 
00125 char *strdup_local(const char *str) {
00126   char *c=(char *)malloc(sizeof(char)*strlen(str)+1);
00127   strcpy(c,str);
00128   return c;
00129 }
00130 
00131 
00132 /* logging stuff */
00133 LogEntry* LogFirst=NULL;
00134 LogEntry* LogLast=NULL;
00135 int logcount=0;
00136 LogListener loglist=NULL;
00137 int setLogListener(LogListener li){
00138     if (loglist)
00139         return 0;
00140     loglist=li;
00141     return 1;
00142 }
00143 void clearLogListener(void) {
00144     loglist=NULL;
00145 }
00146 static const char *const LogLevelTexts[] = {
00147     " DEBUG  ",
00148     "  INFO  ",
00149     "WARNING ",
00150     " ERROR  ",
00151     "CRITICAL",
00152     "UNKNOWN ",
00153 };
00154 static const char *getLogLevelText(LogLevel level) {
00155     return LogLevelTexts[level>LOG_CRITICAL?LOG_CRITICAL+1:level];
00156 }
00157 char *getLogTextRaw(LogLevel level, const char *origin, const char *message) {
00158     static char mybuf[20480];
00159     mybuf[0]='\0';
00160     snprintf(mybuf, sizeof(mybuf), "[%s] (%s) %s\n",getLogLevelText(level),origin,message);
00161     return mybuf;
00162 }
00163 
00164 char *getLogText(const LogEntry *le) {
00165     return getLogTextRaw(le->level,le->origin,le->message);
00166 }
00167 /*
00168  * Logs a message to stderr and save it in memory.
00169  * Or discards the message if it is of no importanse, and none have
00170  * asked to hear messages of that logLevel.
00171  *
00172  * See client.h for possible logLevels.
00173  */
00174 int MINLOG=MINLOGLEVEL;
00175 
00176 
00177 
00178 void LOG(LogLevel level, const char *origin, const char *format, ...)
00179 {
00180 
00181   va_list ap;
00182   static char buf[20480];  /* This needs to be really really big - larger
00183                      * than any other buffer, since that buffer may
00184                      * need to be put in this one.
00185                      */
00186   if (level<MINLOG)
00187     return;
00188 
00189   va_start(ap, format);
00190 
00191   buf[0] = '\0';
00192   vsnprintf(buf, sizeof(buf), format, ap);
00193   /*fprintf(stderr,getLogTextRaw(level,origin,buf));*/
00194   if (strlen(buf)>0){
00195     LogEntry *le = LOG_NEW_ENTRY;
00196     LOG_APPEND(le);
00197     LOG_SETMESSAGE(le,buf);
00198     LOG_SETORIGIN(le,origin);
00199     le->level=level;
00200     fprintf(stderr,getLogText(le));
00201     if (loglist)
00202         (*loglist)(le);
00203   }
00204   va_end(ap);
00205 }
00206 
00207 ChildProcess* FirstChild=NULL;
00208 ChildProcess* LastChild=NULL;
00209 
00210 void purgePipe(ChildProcess* cp, int pipe){
00211     char buf[512];
00212     int len;
00213     len=read (cp->tube[pipe],buf,511);
00214     if (len<1){
00215         if (errno==EAGAIN)
00216             return;
00217         LOG(LOG_ERROR,"common::purgePipe","Child %s: could not read from pipe %d!",cp->name?cp->name:"UNKNOWN",pipe);
00218     }
00219     if (len>0){
00220         char* next;
00221         char* current=buf;
00222         buf[len<512?len:511]='\0';
00223         if (strlen(buf)==0)
00224             return;
00225         for (;;){
00226             if (!current)
00227                 return;
00228             next=strstr(current,"\n");
00229             if (next){
00230                 next[0]='\0';
00231                 next+=strlen("\n");
00232             }
00233             LOG(cp->logger[pipe].level,cp->logger[pipe].name,current);
00234             current=next;
00235         }
00236     }
00237 }
00238 
00239 void monitorChilds(void) {
00240 #ifndef WIN32
00241     ChildProcess* cp=FirstChild;
00242     ChildProcess* last=NULL;
00243     for (;;){
00244         if (!cp)
00245             return; /*no child to monitor*/
00246         if (waitpid(cp->pid,NULL,WNOHANG)){
00247             ChildProcess* next;
00248 
00249             /*pid is dead*/
00250             LOG(LOG_INFO,"common::monitorChilds","Child %s died. Removing and closing pipes",cp->name?cp->name:"UNKNOWN");
00251             if (cp==LastChild)
00252                 LastChild=last;
00253             next=cp->next;
00254             if (last)
00255                 last->next=next;
00256             else
00257                 FirstChild=cp->next;
00258             cp=next;
00259             continue;
00260         }
00261         if (cp->logger[1].log)
00262             purgePipe(cp,1);
00263         if (cp->logger[2].log)
00264             purgePipe(cp,2);
00265         last=cp;
00266         cp=cp->next;
00267     }
00268 #endif
00269 }
00270 
00271 void logPipe(ChildProcess *child, LogLevel level, int pipe){
00272 #ifndef WIN32
00273     char buf[1024];
00274     if ( (pipe<1) || (pipe>2))/*can't log stdin as it's write only*/
00275         return;
00276     if (!child->logger[pipe].name){
00277         snprintf(buf, sizeof(buf), "Child%d::%s::%d",child->pid,child->name?child->name:"NONAME",pipe);
00278         child->logger[pipe].name=strdup(buf);
00279     }
00280     if (fcntl(child->tube[pipe], F_SETFL, O_NDELAY)==-1) {
00281         LOG(LOG_WARNING,"common::logPipe","Error on fcntl.");
00282         child->logger[pipe].log=0; /*We don't log it*/
00283         return;
00284     }
00285     child->logger[pipe].log=1; /*We log it*/
00286     child->logger[pipe].level=level;
00287 #endif
00288 }
00289 
00290 void logChildPipe(ChildProcess* child, LogLevel level, int flag){
00291     if (child->flag & flag & CHILD_STDOUT)
00292         logPipe(child,level,1);
00293     if (child->flag & flag & CHILD_STDERR)
00294         logPipe(child,level,2);
00295 }
00296 
00297 ChildProcess* raiseChild(char* name, int flag){
00298 #ifndef WIN32
00299     ChildProcess* cp;
00300     int pipe_in[2];
00301     int pipe_out[2];
00302     int pipe_err[2];
00303     int pid;
00304     LogLevel deferror;
00305     char *args;
00306     deferror=(flag & CHILD_SILENTFAIL)?LOG_INFO:LOG_ERROR;
00307     LOG(LOG_INFO,"common::raiseChild","Raising %s with flags %d",name,flag);
00308     flag=flag & (~CHILD_SILENTFAIL);
00309     if (flag & (~CHILD_TUBE)){
00310         LOG(LOG_ERROR,"common::raiseChild",
00311                 "Serious CHILD error, unknown pipe requested: 0x%X for %s",
00312                 flag,name);
00313         return NULL; 
00314     }
00315     cp = (ChildProcess*)calloc(1,sizeof(ChildProcess));
00316     if (cp==NULL)
00317         return NULL; /*No log here, we are out of memory for a few DWORDs, no chance to log*/
00318 
00319     /* Separate name and args */
00320     args=name;
00321     while ( *args && *args!=' ' ) ++args;
00322     while ( *args && *args==' ' ) ++args;
00323     if ( *args==0 )
00324         args=NULL;
00325     else
00326         args[-1]=0;
00327     /*let's pipe a bit*/
00328     if (flag&CHILD_STDERR)
00329         if ( pipe(pipe_err) ){
00330             LOG(LOG_ERROR,"common::raiseChild","Couldn't create stderr pipe for %s",name);
00331             free(cp);
00332             return NULL;
00333         }
00334     if (flag&CHILD_STDIN)
00335         if ( pipe(pipe_in) ){
00336             LOG(LOG_ERROR,"common::raiseChild","Couldn't create stdin pipe for %s",name);
00337             if (flag&CHILD_STDERR){
00338                 close(pipe_err[0]);
00339                 close(pipe_err[1]);
00340             }
00341             free(cp);
00342             return NULL;
00343         }
00344     if (flag&CHILD_STDOUT)
00345         if ( pipe(pipe_out) ){
00346             LOG(LOG_ERROR,"common::raiseChild","Couldn't create stdout pipe for %s",name);
00347             if (flag&CHILD_STDERR){
00348                 close(pipe_err[0]);
00349                 close(pipe_err[1]);
00350             }
00351             if (flag&CHILD_STDIN){
00352                 close(pipe_in[0]);
00353                 close(pipe_in[1]);
00354             }
00355             free(cp);
00356             return NULL;
00357         }
00358 
00359     pid=fork();
00360     if (pid==-1){/*failed to fork*/
00361         LOG(LOG_ERROR,"common::raiseChild","Couldn't create child for %s. Closing pipes",name);
00362         if (flag&CHILD_STDIN){
00363             close(pipe_in[0]);
00364             close(pipe_in[1]);
00365         }
00366         if (flag&CHILD_STDOUT){
00367             close(pipe_out[0]);
00368             close(pipe_out[1]);
00369         }
00370         if (flag&CHILD_STDERR){
00371             close(pipe_err[0]);
00372             close(pipe_err[1]);
00373         }
00374         free(cp);
00375         return NULL;
00376     }
00377     if (pid==0){ /*we are the child (yeah))*/
00378         int i;
00379         int r;
00380         char *argv[256];
00381 
00382         /* Fill in argv[] */
00383         argv[0]=name;
00384         i=1;
00385         while (args && *args)
00386         {
00387             argv[i]=args;
00388             ++i;
00389             while ( *args && *args!=' ' ) ++args;
00390             if ( *args )
00391             {
00392                 *args=0;
00393                 ++args;
00394             }
00395             while ( *args && *args==' ' ) ++args;
00396         }
00397         argv[i]=NULL;
00398 
00399         /* Clean up file descriptor space */
00400         if (flag&CHILD_STDERR){
00401             r=dup2(pipe_err[1],2);
00402             close(pipe_err[0]);
00403             if ( r != 2 ) {
00404                 /*No call to log, we are the child, don't mess! Console is only soluce.*/
00405                 fprintf(stderr,"common::raiseChild Failed to set pipe_err as stderr\n");
00406             }
00407         }
00408         if (flag&CHILD_STDOUT){
00409             r=dup2(pipe_out[1],1);
00410             close(pipe_out[0]);
00411             if ( r != 1 ) {
00412                 /*No call to log Father will catch us if he cares of our stderr*/
00413                 fprintf(stderr,"common::raiseChild Failed to set pipe_out as stdout\n");
00414             }
00415         }
00416         if (flag&CHILD_STDIN){
00417             r=dup2(pipe_in[0],0);
00418             close(pipe_in[1]);
00419             if ( r != 0 ) {
00420                 /*No call to log Father will catch us if he cares of our stderr*/
00421                 fprintf(stderr,"common::raiseChild Failed to set pipe_in as stdin\n");
00422             }
00423         }
00424         for (i=3;i<100;++i) close(i);
00425 
00426         /* EXEC */
00427         execvp(argv[0],argv);
00428         exit(-1); /* Should not be reached */
00429     }
00430     /*We are in father here*/
00431     if (flag&CHILD_STDIN){
00432         close (pipe_in[0]); /*close read access to stdin, we are the writer*/
00433         CHILD_PIPEIN(cp)=pipe_in[1];
00434     } else
00435         CHILD_PIPEIN(cp)=-1;
00436 
00437     if (flag&CHILD_STDOUT){
00438         close (pipe_out[1]); /*close write access to stdout, we are the reader*/
00439         CHILD_PIPEOUT(cp)=pipe_out[0];
00440     } else
00441         CHILD_PIPEOUT(cp)=-1;
00442 
00443     if (flag&CHILD_STDERR){
00444         close (pipe_err[1]); /*close write access to stderr, we are the reader*/
00445         CHILD_PIPEERR(cp)=pipe_err[0];
00446     } else
00447         CHILD_PIPEERR(cp)=-1;
00448     cp->pid=pid;
00449     cp->name=strdup(name);
00450     cp->flag=flag;
00451     /*add to chained list*/
00452     if (FirstChild)
00453         LastChild->next=cp;
00454     else
00455         FirstChild=cp;
00456     LastChild=cp;
00457     cp->next=NULL;
00458     return cp;
00459 #else
00460     return NULL;
00461 #endif
00462 }