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