Crossfire Client, Branches  R11627
misc.c
Go to the documentation of this file.
1 const char * const rcsid_common_misc_c =
2  "$Id: misc.c 11626 2009-04-04 12:41:46Z lalo $";
3 /*
4  Crossfire client, a client program for the crossfire program.
5 
6  Copyright (C) 2006,2001 Mark Wedel & Crossfire Development Team
7 
8  This program is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation; either version 2 of the License, or
11  (at your option) any later version.
12 
13  This program is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with this program; if not, write to the Free Software
20  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 
22  The author can be reached via e-mail to crossfire-devel@real-time.com
23 */
24 
25 /*
26  * static char *rcsid_misc_c =
27  * "$Id: misc.c 11626 2009-04-04 12:41:46Z lalo $";
28  */
29 
30 
31 /* Contains misc useful functions that may be useful to various parts
32  * of code, but are not especially tied to it.
33  */
34 
35 #include "client.h"
36 #include <stdarg.h>
37 #include <stdio.h>
38 #include <errno.h>
39 #include <sys/stat.h>
40 #include <sys/types.h>
41 #ifndef WIN32
42 #include <sys/wait.h>
43 #else
44 #include <direct.h>
45 #include <io.h>
46 #endif
47 
48 /*
49  * Verifies that the directory exists, creates it if necessary
50  * Returns -1 on failure
51  */
52 
53 int make_path_to_dir (char *directory)
54 {
55  char buf[MAX_BUF], *cp = buf;
56  struct stat statbuf;
57 
58  if (!directory || !*directory)
59  return -1;
60  strcpy (buf, directory);
61  while ((cp = strchr (cp + 1, (int) '/'))) {
62  *cp = '\0';
63  if (stat (buf, &statbuf) || !S_ISDIR (statbuf.st_mode)) {
64 #ifdef WIN32
65  if (mkdir (buf)) {
66 #else
67  if (mkdir (buf, 0777)) {
68 #endif
69  perror ("Couldn't make path to file");
70  return -1;
71  }
72  } else
73  *cp = '/';
74  }
75  /* Need to make the final component */
76  if (stat (buf, &statbuf) || !S_ISDIR (statbuf.st_mode)) {
77 #ifdef WIN32
78  if (mkdir (buf)) {
79 #else
80  if (mkdir (buf, 0777)) {
81 #endif
82  perror ("Couldn't make path to file");
83  return -1;
84  }
85  }
86  return 0;
87 }
88 
89 
90 /*
91  * If any directories in the given path doesn't exist, they are created.
92  */
93 
94 int make_path_to_file (char *filename)
95 {
96  char buf[MAX_BUF], *cp = buf;
97  struct stat statbuf;
98 
99  if (!filename || !*filename)
100  return -1;
101  strcpy (buf, filename);
102  while ((cp = strchr (cp + 1, (int) '/'))) {
103  *cp = '\0';
104  if (stat (buf, &statbuf) || !S_ISDIR (statbuf.st_mode)) {
105 #ifdef WIN32
106  if (mkdir (buf)) {
107  LOG(LOG_ERROR, "misc.c::make_path_to_file",
108  "Couldn't make path to file: %s", strerror(errno));
109 #else
110  if (mkdir (buf, 0777)) {
111  perror ("Couldn't make path to file");
112 #endif
113  return -1;
114  }
115  }
116  *cp = '/';
117  }
118  return 0;
119 }
120 /*
121  * A replacement of strdup(), since it's not defined at some
122  * unix variants.
123  */
124 
125 char *strdup_local(const char *str) {
126  char *c=(char *)malloc(sizeof(char)*strlen(str)+1);
127  strcpy(c,str);
128  return c;
129 }
130 
131 
132 /* logging stuff */
135 int logcount=0;
138  if (loglist)
139  return 0;
140  loglist=li;
141  return 1;
142 }
143 void clearLogListener(void) {
144  loglist=NULL;
145 }
146 static const char *const LogLevelTexts[] = {
147  " DEBUG ",
148  " INFO ",
149  "WARNING ",
150  " ERROR ",
151  "CRITICAL",
152  "UNKNOWN ",
153 };
154 static const char *getLogLevelText(LogLevel level) {
155  return LogLevelTexts[level>LOG_CRITICAL?LOG_CRITICAL+1:level];
156 }
157 char *getLogTextRaw(LogLevel level, const char *origin, const char *message) {
158  static char mybuf[20480];
159  mybuf[0]='\0';
160  snprintf(mybuf, sizeof(mybuf), "[%s] (%s) %s\n",getLogLevelText(level),origin,message);
161  return mybuf;
162 }
163 
164 char *getLogText(const LogEntry *le) {
165  return getLogTextRaw(le->level,le->origin,le->message);
166 }
167 /*
168  * Logs a message to stderr and save it in memory.
169  * Or discards the message if it is of no importanse, and none have
170  * asked to hear messages of that logLevel.
171  *
172  * See client.h for possible logLevels.
173  */
174 int MINLOG=MINLOGLEVEL;
175 
176 
177 
178 void LOG(LogLevel level, const char *origin, const char *format, ...)
179 {
180 
181  va_list ap;
182  static char buf[20480]; /* This needs to be really really big - larger
183  * than any other buffer, since that buffer may
184  * need to be put in this one.
185  */
186  if (level<MINLOG)
187  return;
188 
189  va_start(ap, format);
190 
191  buf[0] = '\0';
192  vsnprintf(buf, sizeof(buf), format, ap);
193  /*fprintf(stderr,getLogTextRaw(level,origin,buf));*/
194  if (strlen(buf)>0){
195  LogEntry *le = LOG_NEW_ENTRY;
196  LOG_APPEND(le);
197  LOG_SETMESSAGE(le,buf);
198  LOG_SETORIGIN(le,origin);
199  le->level=level;
200  fprintf(stderr,getLogText(le));
201  if (loglist)
202  (*loglist)(le);
203  }
204  va_end(ap);
205 }
206 
209 
210 void purgePipe(ChildProcess* cp, int pipe){
211  char buf[512];
212  int len;
213  len=read (cp->tube[pipe],buf,511);
214  if (len<1){
215  if (errno==EAGAIN)
216  return;
217  LOG(LOG_ERROR,"common::purgePipe","Child %s: could not read from pipe %d!",cp->name?cp->name:"UNKNOWN",pipe);
218  }
219  if (len>0){
220  char* next;
221  char* current=buf;
222  buf[len<512?len:511]='\0';
223  if (strlen(buf)==0)
224  return;
225  for (;;){
226  if (!current)
227  return;
228  next=strstr(current,"\n");
229  if (next){
230  next[0]='\0';
231  next+=strlen("\n");
232  }
233  LOG(cp->logger[pipe].level,cp->logger[pipe].name,current);
234  current=next;
235  }
236  }
237 }
238 
239 void monitorChilds(void) {
240 #ifndef WIN32
242  ChildProcess* last=NULL;
243  for (;;){
244  if (!cp)
245  return; /*no child to monitor*/
246  if (waitpid(cp->pid,NULL,WNOHANG)){
247  ChildProcess* next;
248 
249  /*pid is dead*/
250  LOG(LOG_INFO,"common::monitorChilds","Child %s died. Removing and closing pipes",cp->name?cp->name:"UNKNOWN");
251  if (cp==LastChild)
252  LastChild=last;
253  next=cp->next;
254  if (last)
255  last->next=next;
256  else
257  FirstChild=cp->next;
258  cp=next;
259  continue;
260  }
261  if (cp->logger[1].log)
262  purgePipe(cp,1);
263  if (cp->logger[2].log)
264  purgePipe(cp,2);
265  last=cp;
266  cp=cp->next;
267  }
268 #endif
269 }
270 
271 void logPipe(ChildProcess *child, LogLevel level, int pipe){
272 #ifndef WIN32
273  char buf[1024];
274  if ( (pipe<1) || (pipe>2))/*can't log stdin as it's write only*/
275  return;
276  if (!child->logger[pipe].name){
277  snprintf(buf, sizeof(buf), "Child%d::%s::%d",child->pid,child->name?child->name:"NONAME",pipe);
278  child->logger[pipe].name=strdup(buf);
279  }
280  if (fcntl(child->tube[pipe], F_SETFL, O_NDELAY)==-1) {
281  LOG(LOG_WARNING,"common::logPipe","Error on fcntl.");
282  child->logger[pipe].log=0; /*We don't log it*/
283  return;
284  }
285  child->logger[pipe].log=1; /*We log it*/
286  child->logger[pipe].level=level;
287 #endif
288 }
289 
290 void logChildPipe(ChildProcess* child, LogLevel level, int flag){
291  if (child->flag & flag & CHILD_STDOUT)
292  logPipe(child,level,1);
293  if (child->flag & flag & CHILD_STDERR)
294  logPipe(child,level,2);
295 }
296 
297 ChildProcess* raiseChild(char* name, int flag){
298 #ifndef WIN32
299  ChildProcess* cp;
300  int pipe_in[2];
301  int pipe_out[2];
302  int pipe_err[2];
303  int pid;
304  LogLevel deferror;
305  char *args;
306  deferror=(flag & CHILD_SILENTFAIL)?LOG_INFO:LOG_ERROR;
307  LOG(LOG_INFO,"common::raiseChild","Raising %s with flags %d",name,flag);
308  flag=flag & (~CHILD_SILENTFAIL);
309  if (flag & (~CHILD_TUBE)){
310  LOG(LOG_ERROR,"common::raiseChild",
311  "Serious CHILD error, unknown pipe requested: 0x%X for %s",
312  flag,name);
313  return NULL;
314  }
315  cp = (ChildProcess*)calloc(1,sizeof(ChildProcess));
316  if (cp==NULL)
317  return NULL; /*No log here, we are out of memory for a few DWORDs, no chance to log*/
318 
319  /* Separate name and args */
320  args=name;
321  while ( *args && *args!=' ' ) ++args;
322  while ( *args && *args==' ' ) ++args;
323  if ( *args==0 )
324  args=NULL;
325  else
326  args[-1]=0;
327  /*let's pipe a bit*/
328  if (flag&CHILD_STDERR)
329  if ( pipe(pipe_err) ){
330  LOG(LOG_ERROR,"common::raiseChild","Couldn't create stderr pipe for %s",name);
331  free(cp);
332  return NULL;
333  }
334  if (flag&CHILD_STDIN)
335  if ( pipe(pipe_in) ){
336  LOG(LOG_ERROR,"common::raiseChild","Couldn't create stdin pipe for %s",name);
337  if (flag&CHILD_STDERR){
338  close(pipe_err[0]);
339  close(pipe_err[1]);
340  }
341  free(cp);
342  return NULL;
343  }
344  if (flag&CHILD_STDOUT)
345  if ( pipe(pipe_out) ){
346  LOG(LOG_ERROR,"common::raiseChild","Couldn't create stdout pipe for %s",name);
347  if (flag&CHILD_STDERR){
348  close(pipe_err[0]);
349  close(pipe_err[1]);
350  }
351  if (flag&CHILD_STDIN){
352  close(pipe_in[0]);
353  close(pipe_in[1]);
354  }
355  free(cp);
356  return NULL;
357  }
358 
359  pid=fork();
360  if (pid==-1){/*failed to fork*/
361  LOG(LOG_ERROR,"common::raiseChild","Couldn't create child for %s. Closing pipes",name);
362  if (flag&CHILD_STDIN){
363  close(pipe_in[0]);
364  close(pipe_in[1]);
365  }
366  if (flag&CHILD_STDOUT){
367  close(pipe_out[0]);
368  close(pipe_out[1]);
369  }
370  if (flag&CHILD_STDERR){
371  close(pipe_err[0]);
372  close(pipe_err[1]);
373  }
374  free(cp);
375  return NULL;
376  }
377  if (pid==0){ /*we are the child (yeah))*/
378  int i;
379  int r;
380  char *argv[256];
381 
382  /* Fill in argv[] */
383  argv[0]=name;
384  i=1;
385  while (args && *args)
386  {
387  argv[i]=args;
388  ++i;
389  while ( *args && *args!=' ' ) ++args;
390  if ( *args )
391  {
392  *args=0;
393  ++args;
394  }
395  while ( *args && *args==' ' ) ++args;
396  }
397  argv[i]=NULL;
398 
399  /* Clean up file descriptor space */
400  if (flag&CHILD_STDERR){
401  r=dup2(pipe_err[1],2);
402  close(pipe_err[0]);
403  if ( r != 2 ) {
404  /*No call to log, we are the child, don't mess! Console is only soluce.*/
405  fprintf(stderr,"common::raiseChild Failed to set pipe_err as stderr\n");
406  }
407  }
408  if (flag&CHILD_STDOUT){
409  r=dup2(pipe_out[1],1);
410  close(pipe_out[0]);
411  if ( r != 1 ) {
412  /*No call to log Father will catch us if he cares of our stderr*/
413  fprintf(stderr,"common::raiseChild Failed to set pipe_out as stdout\n");
414  }
415  }
416  if (flag&CHILD_STDIN){
417  r=dup2(pipe_in[0],0);
418  close(pipe_in[1]);
419  if ( r != 0 ) {
420  /*No call to log Father will catch us if he cares of our stderr*/
421  fprintf(stderr,"common::raiseChild Failed to set pipe_in as stdin\n");
422  }
423  }
424  for (i=3;i<100;++i) close(i);
425 
426  /* EXEC */
427  execvp(argv[0],argv);
428  exit(-1); /* Should not be reached */
429  }
430  /*We are in father here*/
431  if (flag&CHILD_STDIN){
432  close (pipe_in[0]); /*close read access to stdin, we are the writer*/
433  CHILD_PIPEIN(cp)=pipe_in[1];
434  } else
435  CHILD_PIPEIN(cp)=-1;
436 
437  if (flag&CHILD_STDOUT){
438  close (pipe_out[1]); /*close write access to stdout, we are the reader*/
439  CHILD_PIPEOUT(cp)=pipe_out[0];
440  } else
441  CHILD_PIPEOUT(cp)=-1;
442 
443  if (flag&CHILD_STDERR){
444  close (pipe_err[1]); /*close write access to stderr, we are the reader*/
445  CHILD_PIPEERR(cp)=pipe_err[0];
446  } else
447  CHILD_PIPEERR(cp)=-1;
448  cp->pid=pid;
449  cp->name=strdup(name);
450  cp->flag=flag;
451  /*add to chained list*/
452  if (FirstChild)
453  LastChild->next=cp;
454  else
455  FirstChild=cp;
456  LastChild=cp;
457  cp->next=NULL;
458  return cp;
459 #else
460  return NULL;
461 #endif
462 }
PipeLog logger[3]
Definition: client.h:415
LogEntry * LogLast
Definition: misc.c:134
ChildProcess * LastChild
Definition: misc.c:208
void monitorChilds(void)
Definition: misc.c:239
int make_path_to_file(char *filename)
Definition: misc.c:94
void logChildPipe(ChildProcess *child, LogLevel level, int flag)
Definition: misc.c:290
char * name
Definition: client.h:411
void logPipe(ChildProcess *child, LogLevel level, int pipe)
Definition: misc.c:271
ChildProcess * FirstChild
Definition: misc.c:207
LogLevel
Definition: client.h:367
#define LOG_SETMESSAGE(_Entry, _msg)
Definition: client.h:396
void purgePipe(ChildProcess *cp, int pipe)
Definition: misc.c:210
int setLogListener(LogListener li)
Definition: misc.c:137
#define CHILD_TUBE
Definition: client.h:409
LogLevel level
Definition: client.h:401
void LOG(LogLevel level, const char *origin, const char *format,...)
Definition: misc.c:178
int logcount
Definition: misc.c:135
#define CHILD_PIPEIN(__child)
Definition: client.h:419
#define CHILD_STDOUT
Definition: client.h:406
#define CHILD_PIPEERR(__child)
Definition: client.h:421
#define CHILD_STDERR
Definition: client.h:407
#define LOG_SETORIGIN(_Entry, _orig)
Definition: client.h:397
char * getLogText(const LogEntry *le)
Definition: misc.c:164
char * name
Definition: client.h:400
char * name
Definition: image.c:61
#define CHILD_STDIN
Definition: client.h:405
struct ChildProcess * next
Definition: client.h:416
void clearLogListener(void)
Definition: misc.c:143
char * getLogTextRaw(LogLevel level, const char *origin, const char *message)
Definition: misc.c:157
#define CHILD_SILENTFAIL
Definition: client.h:408
char * message
Definition: client.h:372
#define LOG_APPEND(_Entry)
Definition: client.h:382
const char *const rcsid_common_misc_c
Definition: misc.c:1
#define CHILD_PIPEOUT(__child)
Definition: client.h:420
ChildProcess * raiseChild(char *name, int flag)
Definition: misc.c:297
#define MAX_BUF
Definition: client-types.h:128
int flag
Definition: client.h:412
int tube[3]
Definition: client.h:414
LogListener loglist
Definition: misc.c:136
char * origin
Definition: client.h:373
void(* LogListener)(LogEntry *)
Definition: client.h:380
int log
Definition: client.h:402
static const char *const LogLevelTexts[]
Definition: misc.c:146
char * strdup_local(const char *str)
Definition: misc.c:125
#define LOG_NEW_ENTRY
Definition: client.h:395
int MINLOG
Definition: misc.c:174
int make_path_to_dir(char *directory)
Definition: misc.c:53
LogLevel level
Definition: client.h:374
static const char * getLogLevelText(LogLevel level)
Definition: misc.c:154
LogEntry * LogFirst
Definition: misc.c:133