Crossfire Client, Branch
R11627
|
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 }