]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/inspircd_io.cpp
Added pid file support, and documentation for it
[user/henk/code/inspircd.git] / src / inspircd_io.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  Inspire is copyright (C) 2002-2004 ChatSpike-Dev.
6  *                       E-mail:
7  *                <brain@chatspike.net>
8  *                <Craig@chatspike.net>
9  *     
10  * Written by Craig Edwards, Craig McLure, and others.
11  * This program is free but copyrighted software; see
12  *            the file COPYING for details.
13  *
14  * ---------------------------------------------------
15  */
16
17 #include <sys/time.h>
18 #include <sys/resource.h>
19 #include <sys/types.h>
20 #include <string>
21 #include <unistd.h>
22 #include <sstream>
23 #include <iostream>
24 #include <fstream>
25 #include "inspircd.h"
26 #include "inspircd_io.h"
27 #include "inspircd_util.h"
28 #include "inspstring.h"
29
30 using namespace std;
31
32 extern FILE *log_file;
33
34 void WriteOpers(char* text, ...);
35
36 void Exit (int status)
37 {
38   if (log_file)
39         fclose(log_file);
40   send_error("Server shutdown.");
41   exit (status);
42 }
43
44 void Killed(int status)
45 {
46   if (log_file)
47         fclose(log_file);
48   send_error("Server terminated.");
49   exit(status);
50 }
51
52 void Rehash(int status)
53 {
54   WriteOpers("Rehashing config file %s due to SIGHUP",CONFIG_FILE);
55   ReadConfig(false,NULL);
56 }
57
58
59
60 void Start (void)
61 {
62   printf("\033[1;37mInspire Internet Relay Chat Server, compiled " __DATE__ " at " __TIME__ "\n");
63   printf("(C) ChatSpike Development team.\033[0;37m\n\n");
64   printf("\033[1;37mDevelopers:\033[0;37m     Brain, FrostyCoolSlug\n");
65   printf("\033[1;37mDocumentation:\033[0;37m  FrostyCoolSlug, w00t\n");
66   printf("\033[1;37mTesters:\033[0;37m        typobox43, piggles, Lord_Zathras, CC\n");
67   printf("\033[1;37mName concept:\033[0;37m   Lord_Zathras\n\n");
68 }
69
70 void WritePID(std::string filename)
71 {
72         ofstream outfile(filename.c_str());
73         if (outfile.is_open())
74         {
75                 outfile << getpid();
76                 outfile.close();
77         }
78         else
79         {
80                 printf("Failed to write PID-file '%s', exiting.\n",filename.c_str());
81                 Exit(0);
82         }
83 }
84
85 void DeadPipe(int status)
86 {
87   signal (SIGPIPE, DeadPipe);
88 }
89
90 int DaemonSeed (void)
91 {
92   int childpid;
93   signal (SIGALRM, SIG_IGN);
94   signal (SIGHUP, Rehash);
95   signal (SIGPIPE, DeadPipe);
96   signal (SIGTERM, Exit);
97   signal (SIGABRT, Exit);
98   signal (SIGSEGV, Error);
99   signal (SIGURG, Exit);
100   signal (SIGKILL, Exit);
101   if ((childpid = fork ()) < 0)
102     return (ERROR);
103   else if (childpid > 0)
104     exit (0);
105   setsid ();
106   umask (007);
107   /* close stdout, stdin, stderr */
108   close(0);
109   close(1);
110   close(2);
111
112   setpriority(PRIO_PROCESS,(int)getpid(),15); /* ircd sets to low process priority so it doesnt hog the box */
113   
114   return (TRUE);
115 }
116
117
118 /* Make Sure Modules Are Avaliable!
119  * (BugFix By Craig.. See? I do work! :p)
120  * Modified by brain, requires const char*
121  * to work with other API functions
122  */
123
124 bool FileExists (const char* file)
125 {
126   FILE *input;
127   
128   if ((input = fopen (file, "r")) == NULL) { return(false); }
129   else { fclose (input); return(true); }
130 }
131
132 /* ConfProcess does the following things to a config line in the following order:
133  *
134  * Processes the line for syntax errors as shown below
135  *      (1) Line void of quotes or equals (a malformed, illegal tag format)
136  *      (2) Odd number of quotes on the line indicating a missing quote
137  *      (3) number of equals signs not equal to number of quotes / 2 (missing an equals sign)
138  *      (4) Spaces between the opening bracket (<) and the keyword
139  *      (5) Spaces between a keyword and an equals sign
140  *      (6) Spaces between an equals sign and a quote
141  * Removes trailing spaces
142  * Removes leading spaces
143  * Converts tabs to spaces
144  * Turns multiple spaces that are outside of quotes into single spaces
145  */
146
147 std::string ConfProcess(char* buffer, long linenumber, std::stringstream* errorstream, bool &error, std::string filename)
148 {
149         long number_of_quotes = 0;
150         long number_of_equals = 0;
151         bool has_open_bracket = false;
152         bool in_quotes = false;
153         error = false;
154         if (!buffer)
155         {
156                 return "";
157         }
158         // firstly clean up the line by stripping spaces from the start and end and converting tabs to spaces
159         for (int d = 0; d < strlen(buffer); d++)
160                 if ((buffer[d]) == 9)
161                         buffer[d] = ' ';
162         while ((buffer[0] == ' ') && (strlen(buffer)>0)) buffer++;
163         while ((buffer[strlen(buffer)-1] == ' ') && (strlen(buffer)>0)) buffer[strlen(buffer)-1] = '\0';
164         // empty lines are syntactically valid
165         if (!strcmp(buffer,""))
166                 return "";
167         else if (buffer[0] == '#')
168                 return "";
169         for (int c = 0; c < strlen(buffer); c++)
170         {
171                 // convert all spaces that are OUTSIDE quotes into hardspace (0xA0) as this will make them easier to
172                 // search and replace later :)
173                 if ((!in_quotes) && (buffer[c] == ' '))
174                         buffer[c] = '\xA0';
175                 if ((buffer[c] == '<') && (!in_quotes))
176                 {
177                         has_open_bracket = true;
178                         if (strlen(buffer) == 1)
179                         {
180                                 *errorstream << "Tag without identifier at " << filename << ":" << linenumber << endl;
181                                 error = true;
182                                 return "";
183                         }
184                         else if ((tolower(buffer[c+1]) < 'a') || (tolower(buffer[c+1]) > 'z'))
185                         {
186                                 *errorstream << "Invalid characters in identifier at " << filename << ":" << linenumber << endl;
187                                 error = true;
188                                 return "";
189                         }
190                 }
191                 if (buffer[c] == '"')
192                 {
193                         number_of_quotes++;
194                         in_quotes = (!in_quotes);
195                 }
196                 if ((buffer[c] == '=') && (!in_quotes))
197                 {
198                         number_of_equals++;
199                         if (strlen(buffer) == c)
200                         {
201                                 *errorstream << "Variable without a value at " << filename << ":" << linenumber << endl;
202                                 error = true;
203                                 return "";
204                         }
205                         else if (buffer[c+1] != '"')
206                         {
207                                 *errorstream << "Variable name not followed immediately by its value at " << filename << ":" << linenumber << endl;
208                                 error = true;
209                                 return "";
210                         }
211                         else if (!c)
212                         {
213                                 *errorstream << "Value without a variable (line starts with '=') at " << filename << ":" << linenumber << endl;
214                                 error = true;
215                                 return "";
216                         }
217                         else if (buffer[c-1] == '\xA0')
218                         {
219                                 *errorstream << "Variable name not followed immediately by its value at " << filename << ":" << linenumber << endl;
220                                 error = true;
221                                 return "";
222                         }
223                 }
224         }
225         // no quotes, and no equals. something freaky.
226         if ((!number_of_quotes) || (!number_of_equals) && (strlen(buffer)>2) && (buffer[0]=='<'))
227         {
228                 *errorstream << "Malformed tag at " << filename << ":" << linenumber << endl;
229                 error = true;
230                 return "";
231         }
232         // odd number of quotes. thats just wrong.
233         if ((number_of_quotes % 2) != 0)
234         {
235                 *errorstream << "Missing \" at " << filename << ":" << linenumber << endl;
236                 error = true;
237                 return "";
238         }
239         if (number_of_equals < (number_of_quotes/2))
240         {
241                 *errorstream << "Missing '=' at " << filename << ":" << linenumber << endl;
242         }
243         if (number_of_equals > (number_of_quotes/2))
244         {
245                 *errorstream << "Too many '=' at " << filename << ":" << linenumber << endl;
246         }
247
248         std::string parsedata = buffer;
249         // turn multispace into single space
250         while (parsedata.find("\xA0\xA0") != std::string::npos)
251         {
252                 parsedata.erase(parsedata.find("\xA0\xA0"),1);
253         }
254
255         // turn our hardspace back into softspace
256         for (int d = 0; d < parsedata.length(); d++)
257         {
258                 if (parsedata[d] == '\xA0')
259                         parsedata[d] = ' ';
260         }
261
262         // and we're done, the line is fine!
263         return parsedata;
264 }
265
266 bool LoadConf(const char* filename, std::stringstream *target, std::stringstream* errorstream)
267 {
268         target->str("");
269         errorstream->str("");
270         long linenumber = 1;
271         FILE* conf = fopen(filename,"r");
272         if (!FileExists(filename))
273         {
274                 *errorstream << "File " << filename << " not found." << endl;
275                 return false;
276         }
277         char buffer[MAXBUF];
278         if (conf)
279         {
280                 while (!feof(conf))
281                 {
282                         if (fgets(buffer, MAXBUF, conf))
283                         {
284                                 if ((!feof(conf)) && (buffer) && (strlen(buffer)))
285                                 {
286                                         if ((buffer[0] != '#') && (buffer[0] != '\r')  && (buffer[0] != '\n'))
287                                         {
288                                                 bool error = false;
289                                                 std::string data = ConfProcess(buffer,linenumber++,errorstream,error,filename);
290                                                 if (error)
291                                                 {
292                                                         return false;
293                                                 }
294                                                 *target << data;
295                                         }
296                                         else linenumber++;
297                                 }
298                         }
299                 }
300                 fclose(conf);
301         }
302         target->seekg(0);
303         return true;
304 }
305
306 /* Counts the number of tags of a certain type within the config file, e.g. to enumerate opers */
307
308 int EnumConf(std::stringstream *config, const char* tag)
309 {
310         int ptr = 0;
311         char buffer[MAXBUF], c_tag[MAXBUF], c, lastc;
312         int in_token, in_quotes, tptr, j, idx = 0;
313         char* key;
314
315         const char* buf = config->str().c_str();
316         long bptr = 0;
317         long len = strlen(buf);
318         
319         ptr = 0;
320         in_token = 0;
321         in_quotes = 0;
322         lastc = '\0';
323         while (bptr<len)
324         {
325                 lastc = c;
326                 c = buf[bptr++];
327                 if ((c == '#') && (lastc == '\n'))
328                 {
329                         while ((c != '\n') && (bptr<len))
330                         {
331                                 lastc = c;
332                                 c = buf[bptr++];
333                         }
334                 }
335                 if ((c == '<') && (!in_quotes))
336                 {
337                         tptr = 0;
338                         in_token = 1;
339                         do {
340                                 c = buf[bptr++];
341                                 if (c != ' ')
342                                 {
343                                         c_tag[tptr++] = c;
344                                         c_tag[tptr] = '\0';
345                                 }
346                         } while (c != ' ');
347                 }
348                 if (c == '"')
349                 {
350                         in_quotes = (!in_quotes);
351                 }
352                 if ((c == '>') && (!in_quotes))
353                 {
354                         in_token = 0;
355                         if (!strcmp(c_tag,tag))
356                         {
357                                 /* correct tag, but wrong index */
358                                 idx++;
359                         }
360                         c_tag[0] = '\0';
361                         buffer[0] = '\0';
362                         ptr = 0;
363                         tptr = 0;
364                 }
365                 if (c != '>')
366                 {
367                         if ((in_token) && (c != '\n') && (c != '\r'))
368                         {
369                                 buffer[ptr++] = c;
370                                 buffer[ptr] = '\0';
371                         }
372                 }
373         }
374         return idx;
375 }
376
377 /* Counts the number of values within a certain tag */
378
379 int EnumValues(std::stringstream *config, const char* tag, int index)
380 {
381         int ptr = 0;
382         char buffer[MAXBUF], c_tag[MAXBUF], c, lastc;
383         int in_token, in_quotes, tptr, j, idx = 0;
384         char* key;
385         
386         bool correct_tag = false;
387         int num_items = 0;
388
389         const char* buf = config->str().c_str();
390         long bptr = 0;
391         long len = strlen(buf);
392         
393         ptr = 0;
394         in_token = 0;
395         in_quotes = 0;
396         lastc = '\0';
397         while (bptr<len)
398         {
399                 lastc = c;
400                 c = buf[bptr++];
401                 if ((c == '#') && (lastc == '\n'))
402                 {
403                         while ((c != '\n') && (bptr<len))
404                         {
405                                 lastc = c;
406                                 c = buf[bptr++];
407                         }
408                 }
409                 if ((c == '<') && (!in_quotes))
410                 {
411                         tptr = 0;
412                         in_token = 1;
413                         do {
414                                 c = buf[bptr++];
415                                 if (c != ' ')
416                                 {
417                                         c_tag[tptr++] = c;
418                                         c_tag[tptr] = '\0';
419                                         
420                                         if ((!strcmp(c_tag,tag)) && (idx == index))
421                                         {
422                                                 correct_tag = true;
423                                         }
424                                 }
425                         } while (c != ' ');
426                 }
427                 if (c == '"')
428                 {
429                         in_quotes = (!in_quotes);
430                 }
431                 
432                 if ( (correct_tag) && (!in_quotes) && ( (c == ' ') || (c == '\n') || (c == '\r') ) )
433                 {
434                         num_items++;
435                 }
436                 if ((c == '>') && (!in_quotes))
437                 {
438                         in_token = 0;
439                         if (correct_tag)
440                                 correct_tag = false;
441                         if (!strcmp(c_tag,tag))
442                         {
443                                 /* correct tag, but wrong index */
444                                 idx++;
445                         }
446                         c_tag[0] = '\0';
447                         buffer[0] = '\0';
448                         ptr = 0;
449                         tptr = 0;
450                 }
451                 if (c != '>')
452                 {
453                         if ((in_token) && (c != '\n') && (c != '\r'))
454                         {
455                                 buffer[ptr++] = c;
456                                 buffer[ptr] = '\0';
457                         }
458                 }
459         }
460         return num_items+1;
461 }
462
463
464
465 int ConfValueEnum(char* tag, std::stringstream* config)
466 {
467         return EnumConf(config,tag);
468 }
469
470
471
472 /* Retrieves a value from the config file. If there is more than one value of the specified
473  * key and section (e.g. for opers etc) then the index value specifies which to retreive, e.g.
474  *
475  * ConfValue("oper","name",2,result);
476  */
477
478 int ReadConf(std::stringstream *config, const char* tag, const char* var, int index, char *result)
479 {
480         int ptr = 0;
481         char buffer[65535], c_tag[MAXBUF], c, lastc;
482         int in_token, in_quotes, tptr, j, idx = 0;
483         char* key;
484
485         const char* buf = config->str().c_str();
486         long bptr = 0;
487         long len = strlen(buf);
488         
489         ptr = 0;
490         in_token = 0;
491         in_quotes = 0;
492         lastc = '\0';
493         c_tag[0] = '\0';
494         buffer[0] = '\0';
495         while (bptr<len)
496         {
497                 lastc = c;
498                 c = buf[bptr++];
499                 // FIX: Treat tabs as spaces
500                 if (c == 9)
501                         c = 32;
502                 if ((c == '<') && (!in_quotes))
503                 {
504                         tptr = 0;
505                         in_token = 1;
506                         do {
507                                 c = buf[bptr++];
508                                 if (c != ' ')
509                                 {
510                                         c_tag[tptr++] = c;
511                                         c_tag[tptr] = '\0';
512                                 }
513                         // FIX: Tab can follow a tagname as well as space.
514                         } while ((c != ' ') && (c != 9));
515                 }
516                 if (c == '"')
517                 {
518                         in_quotes = (!in_quotes);
519                 }
520                 if ((c == '>') && (!in_quotes))
521                 {
522                         in_token = 0;
523                         if (idx == index)
524                         {
525                                 if (!strcmp(c_tag,tag))
526                                 {
527                                         if ((buffer) && (c_tag) && (var))
528                                         {
529                                                 key = strstr(buffer,var);
530                                                 if (!key)
531                                                 {
532                                                         /* value not found in tag */
533                                                         strcpy(result,"");
534                                                         return 0;
535                                                 }
536                                                 else
537                                                 {
538                                                         key+=strlen(var);
539                                                         while (key[0] !='"')
540                                                         {
541                                                                 if (!strlen(key))
542                                                                 {
543                                                                         /* missing quote */
544                                                                         strcpy(result,"");
545                                                                         return 0;
546                                                                 }
547                                                                 key++;
548                                                         }
549                                                         key++;
550                                                         for (j = 0; j < strlen(key); j++)
551                                                         {
552                                                                 if (key[j] == '"')
553                                                                 {
554                                                                         key[j] = '\0';
555                                                                 }
556                                                         }
557                                                         strlcpy(result,key,MAXBUF);
558                                                         return 1;
559                                                 }
560                                         }
561                                 }
562                         }
563                         if (!strcmp(c_tag,tag))
564                         {
565                                 /* correct tag, but wrong index */
566                                 idx++;
567                         }
568                         c_tag[0] = '\0';
569                         buffer[0] = '\0';
570                         ptr = 0;
571                         tptr = 0;
572                 }
573                 if (c != '>')
574                 {
575                         if ((in_token) && (c != '\n') && (c != '\r'))
576                         {
577                                 buffer[ptr++] = c;
578                                 buffer[ptr] = '\0';
579                         }
580                 }
581         }
582         strcpy(result,""); // value or its tag not found at all
583         return 0;
584 }
585
586
587
588 int ConfValue(char* tag, char* var, int index, char *result,std::stringstream *config)
589 {
590         ReadConf(config, tag, var, index, result);
591         return 0;
592 }
593
594
595
596 /* This will bind a socket to a port. It works for UDP/TCP */
597 int BindSocket (int sockfd, struct sockaddr_in client, struct sockaddr_in server, int port, char* addr)
598 {
599   bzero((char *)&server,sizeof(server));
600   struct in_addr addy;
601   inet_aton(addr,&addy);
602
603   server.sin_family = AF_INET;
604   if (!strcmp(addr,""))
605   {
606           server.sin_addr.s_addr = htonl(INADDR_ANY);
607   }
608   else
609   {
610           server.sin_addr = addy;
611   }
612
613   server.sin_port = htons(port);
614
615   if (bind(sockfd,(struct sockaddr*)&server,sizeof(server))<0)
616   {
617     return(ERROR);
618   }
619   else
620   {
621     listen(sockfd,5);
622     return(TRUE);
623   }
624 }
625
626
627 /* Open a TCP Socket */
628 int OpenTCPSocket (void)
629 {
630   int sockfd;
631   int on = 0;
632   struct linger linger = { 0 };
633   
634   if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
635     return (ERROR);
636   else
637   {
638     setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
639     /* This is BSD compatible, setting l_onoff to 0 is *NOT* http://web.irc.org/mla/ircd-dev/msg02259.html */
640     linger.l_onoff = 1;
641     linger.l_linger = 0;
642     setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (const char*)&linger,sizeof(linger));
643     return (sockfd);
644   }
645 }
646