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