]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/inspircd_io.cpp
More experimental gubbins
[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 "inspircd_config.h"
20 #include <sys/time.h>
21 #include <sys/resource.h>
22 #include <sys/types.h>
23 #include <string>
24 #include <unistd.h>
25 #include <sstream>
26 #include <iostream>
27 #include <fstream>
28 #include "inspircd.h"
29 #include "inspircd_io.h"
30 #include "inspircd_util.h"
31 #include "inspstring.h"
32 #include "helperfuncs.h"
33
34 extern FILE *log_file;
35 extern int boundPortCount;
36 extern int openSockfd[MAXSOCKS];
37 extern time_t TIME;
38 extern bool unlimitcore;
39 extern int MaxConn;
40 std::vector<std::string> include_stack;
41
42 void WriteOpers(char* text, ...);
43
44 void Exit (int status)
45 {
46         if (log_file)
47                 fclose(log_file);
48         send_error("Server shutdown.");
49         exit (status);
50 }
51
52 void Killed(int status)
53 {
54         if (log_file)
55                 fclose(log_file);
56         send_error("Server terminated.");
57         exit(status);
58 }
59
60 void Rehash(int status)
61 {
62         WriteOpers("Rehashing config file %s due to SIGHUP",CONFIG_FILE);
63         ReadConfig(false,NULL);
64 }
65
66
67
68 void Start (void)
69 {
70         printf("\033[1;32mInspire Internet Relay Chat Server, compiled %s at %s\n",__DATE__,__TIME__);
71         printf("(C) ChatSpike Development team.\033[0m\n\n");
72         printf("Developers:\033[1;32m     Brain, FrostyCoolSlug\033[0m\n");
73         printf("Documentation:\033[1;32m  FrostyCoolSlug, w00t\033[0m\n");
74         printf("Testers:\033[1;32m        typobox43, piggles, Lord_Zathras, CC\033[0m\n");
75         printf("Name concept:\033[1;32m   Lord_Zathras\033[0m\n\n");
76 }
77
78 void WritePID(std::string filename)
79 {
80         ofstream outfile(filename.c_str());
81         if (outfile.is_open())
82         {
83                 outfile << getpid();
84                 outfile.close();
85         }
86         else
87         {
88                 printf("Failed to write PID-file '%s', exiting.\n",filename.c_str());
89                 log(DEFAULT,"Failed to write PID-file '%s', exiting.",filename.c_str());
90                 Exit(0);
91         }
92 }
93
94
95 int DaemonSeed (void)
96 {
97         int childpid;
98         signal (SIGALRM, SIG_IGN);
99         signal (SIGHUP, Rehash);
100         signal (SIGPIPE, SIG_IGN);
101         signal (SIGTERM, Exit);
102         signal (SIGSEGV, Error);
103         if ((childpid = fork ()) < 0)
104                 return (ERROR);
105         else if (childpid > 0)
106                 exit (0);
107         setsid ();
108         umask (007);
109         printf("InspIRCd Process ID: \033[1;32m%lu\033[0m\n",(unsigned long)getpid());
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 (unsigned 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
186         // empty lines are syntactically valid, as are comments
187         if (!(*buffer) || buffer[0] == '#')
188                 return "";
189
190         for (unsigned 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 (unsigned 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 int fgets_safe(char* buffer, size_t maxsize, FILE* &file)
288 {
289         char c_read = '\0';
290         unsigned int bufptr = 0;
291         while ((!feof(file)) && (c_read != '\n') && (c_read != '\r') && (bufptr < maxsize))
292         {
293                 c_read = fgetc(file);
294                 if ((c_read != '\n') && (c_read != '\r'))
295                         buffer[bufptr++] = c_read;
296         }
297         buffer[bufptr] = '\0';
298         return bufptr;
299 }
300
301 bool LoadConf(const char* filename, std::stringstream *target, std::stringstream* errorstream)
302 {
303         target->str("");
304         errorstream->str("");
305         long linenumber = 1;
306         // first, check that the file exists before we try to do anything with it
307         if (!FileExists(filename))
308         {
309                 *errorstream << "File " << filename << " not found." << endl;
310                 return false;
311         }
312         // Fix the chmod of the file to restrict it to the current user and group
313         chmod(filename,0600);
314         for (unsigned int t = 0; t < include_stack.size(); t++)
315         {
316                 if (std::string(filename) == include_stack[t])
317                 {
318                         *errorstream << "File " << filename << " is included recursively (looped inclusion)." << endl;
319                         return false;
320                 }
321         }
322         include_stack.push_back(filename);
323         // now open it
324         FILE* conf = fopen(filename,"r");
325         char buffer[MAXBUF];
326         if (conf)
327         {
328                 while (!feof(conf))
329                 {
330                         if (fgets_safe(buffer, MAXBUF, conf))
331                         {
332                                 if ((!feof(conf)) && (buffer) && (strlen(buffer)))
333                                 {
334                                         if ((buffer[0] != '#') && (buffer[0] != '\r')  && (buffer[0] != '\n'))
335                                         {
336                                                 if (!strncmp(buffer,"<include file=\"",15))
337                                                 {
338                                                         char* buf = buffer;
339                                                         char confpath[10240],newconf[10240];
340                                                         // include file directive
341                                                         buf += 15;      // advance to filename
342                                                         for (unsigned int j = 0; j < strlen(buf); j++)
343                                                         {
344                                                                 if (buf[j] == '\\')
345                                                                         buf[j] = '/';
346                                                                 if (buf[j] == '"')
347                                                                 {
348                                                                         buf[j] = '\0';
349                                                                         break;
350                                                                 }
351                                                         }
352                                                         log(DEFAULT,"Opening included file '%s'",buf);
353                                                         if (*buf != '/')
354                                                         {
355                                                                 strlcpy(confpath,CONFIG_FILE,10240);
356                                                                 if (strstr(confpath,"/inspircd.conf"))
357                                                                 {
358                                                                         // leaves us with just the path
359                                                                         *(strstr(confpath,"/inspircd.conf")) = '\0';
360                                                                 }
361                                                                 snprintf(newconf,10240,"%s/%s",confpath,buf);
362                                                         }
363                                                         else snprintf(newconf,10240,"%s",buf);
364                                                         std::stringstream merge(stringstream::in | stringstream::out);
365                                                         // recursively call LoadConf and get the new data, use the same errorstream
366                                                         if (LoadConf(newconf, &merge, errorstream))
367                                                         {
368                                                                 // append to the end of the file
369                                                                 std::string newstuff = merge.str();
370                                                                 *target << newstuff;
371                                                         }
372                                                         else
373                                                         {
374                                                                 // the error propogates up to its parent recursively
375                                                                 // causing the config reader to bail at the top level.
376                                                                 fclose(conf);
377                                                                 return false;
378                                                         }
379                                                 }
380                                                 else
381                                                 {
382                                                         bool error = false;
383                                                         std::string data = ConfProcess(buffer,linenumber++,errorstream,error,filename);
384                                                         if (error)
385                                                         {
386                                                                 return false;
387                                                         }
388                                                         *target << data;
389                                                 }
390                                         }
391                                         else linenumber++;
392                                 }
393                         }
394                 }
395                 fclose(conf);
396         }
397         target->seekg(0);
398         return true;
399 }
400
401 /* Counts the number of tags of a certain type within the config file, e.g. to enumerate opers */
402
403 int EnumConf(std::stringstream *config, const char* tag)
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         const char* buf = config->str().c_str();
410         long bptr = 0;
411         long len = strlen(buf);
412         
413         ptr = 0;
414         in_token = 0;
415         in_quotes = 0;
416         lastc = '\0';
417         while (bptr<len)
418         {
419                 lastc = c;
420                 c = buf[bptr++];
421                 if ((c == '#') && (lastc == '\n'))
422                 {
423                         while ((c != '\n') && (bptr<len))
424                         {
425                                 lastc = c;
426                                 c = buf[bptr++];
427                         }
428                 }
429                 if ((c == '<') && (!in_quotes))
430                 {
431                         tptr = 0;
432                         in_token = 1;
433                         do {
434                                 c = buf[bptr++];
435                                 if (c != ' ')
436                                 {
437                                         c_tag[tptr++] = c;
438                                         c_tag[tptr] = '\0';
439                                 }
440                         } while (c != ' ');
441                 }
442                 if (c == '"')
443                 {
444                         in_quotes = (!in_quotes);
445                 }
446                 if ((c == '>') && (!in_quotes))
447                 {
448                         in_token = 0;
449                         if (!strcmp(c_tag,tag))
450                         {
451                                 /* correct tag, but wrong index */
452                                 idx++;
453                         }
454                         c_tag[0] = '\0';
455                         buffer[0] = '\0';
456                         ptr = 0;
457                         tptr = 0;
458                 }
459                 if (c != '>')
460                 {
461                         if ((in_token) && (c != '\n') && (c != '\r'))
462                         {
463                                 buffer[ptr++] = c;
464                                 buffer[ptr] = '\0';
465                         }
466                 }
467         }
468         return idx;
469 }
470
471 /* Counts the number of values within a certain tag */
472
473 int EnumValues(std::stringstream *config, const char* tag, int index)
474 {
475         int ptr = 0;
476         char buffer[MAXBUF], c_tag[MAXBUF], c, lastc;
477         int in_token, in_quotes, tptr, idx = 0;
478         
479         bool correct_tag = false;
480         int num_items = 0;
481
482         const char* buf = config->str().c_str();
483         long bptr = 0;
484         long len = strlen(buf);
485         
486         ptr = 0;
487         in_token = 0;
488         in_quotes = 0;
489         lastc = '\0';
490         while (bptr<len)
491         {
492                 lastc = c;
493                 c = buf[bptr++];
494                 if ((c == '#') && (lastc == '\n'))
495                 {
496                         while ((c != '\n') && (bptr<len))
497                         {
498                                 lastc = c;
499                                 c = buf[bptr++];
500                         }
501                 }
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                                         if ((!strcmp(c_tag,tag)) && (idx == index))
514                                         {
515                                                 correct_tag = true;
516                                         }
517                                 }
518                         } while (c != ' ');
519                 }
520                 if (c == '"')
521                 {
522                         in_quotes = (!in_quotes);
523                 }
524                 
525                 if ( (correct_tag) && (!in_quotes) && ( (c == ' ') || (c == '\n') || (c == '\r') ) )
526                 {
527                         num_items++;
528                 }
529                 if ((c == '>') && (!in_quotes))
530                 {
531                         in_token = 0;
532                         if (correct_tag)
533                                 correct_tag = false;
534                         if (!strcmp(c_tag,tag))
535                         {
536                                 /* correct tag, but wrong index */
537                                 idx++;
538                         }
539                         c_tag[0] = '\0';
540                         buffer[0] = '\0';
541                         ptr = 0;
542                         tptr = 0;
543                 }
544                 if (c != '>')
545                 {
546                         if ((in_token) && (c != '\n') && (c != '\r'))
547                         {
548                                 buffer[ptr++] = c;
549                                 buffer[ptr] = '\0';
550                         }
551                 }
552         }
553         return num_items+1;
554 }
555
556
557
558 int ConfValueEnum(char* tag, std::stringstream* config)
559 {
560         return EnumConf(config,tag);
561 }
562
563
564
565 /* Retrieves a value from the config file. If there is more than one value of the specified
566  * key and section (e.g. for opers etc) then the index value specifies which to retreive, e.g.
567  *
568  * ConfValue("oper","name",2,result);
569  */
570
571 int ReadConf(std::stringstream *config, const char* tag, const char* var, int index, char *result)
572 {
573         int ptr = 0;
574         char buffer[65535], c_tag[MAXBUF], c, lastc;
575         int in_token, in_quotes, tptr, idx = 0;
576         char* key;
577
578         const char* buf = config->str().c_str();
579         long bptr = 0;
580         long len = config->str().length();
581         
582         ptr = 0;
583         in_token = 0;
584         in_quotes = 0;
585         lastc = '\0';
586         c_tag[0] = '\0';
587         buffer[0] = '\0';
588         while (bptr<len)
589         {
590                 lastc = c;
591                 c = buf[bptr++];
592                 // FIX: Treat tabs as spaces
593                 if (c == 9)
594                         c = 32;
595                 if ((c == '<') && (!in_quotes))
596                 {
597                         tptr = 0;
598                         in_token = 1;
599                         do {
600                                 c = buf[bptr++];
601                                 if (c != ' ')
602                                 {
603                                         c_tag[tptr++] = c;
604                                         c_tag[tptr] = '\0';
605                                 }
606                         // FIX: Tab can follow a tagname as well as space.
607                         } while ((c != ' ') && (c != 9));
608                 }
609                 if (c == '"')
610                 {
611                         in_quotes = (!in_quotes);
612                 }
613                 if ((c == '>') && (!in_quotes))
614                 {
615                         in_token = 0;
616                         if (idx == index)
617                         {
618                                 if (!strcmp(c_tag,tag))
619                                 {
620                                         if ((buffer) && (c_tag) && (var))
621                                         {
622                                                 key = strstr(buffer,var);
623                                                 if (!key)
624                                                 {
625                                                         /* value not found in tag */
626                                                         strcpy(result,"");
627                                                         return 0;
628                                                 }
629                                                 else
630                                                 {
631                                                         key+=strlen(var);
632                                                         while (key[0] !='"')
633                                                         {
634                                                                 if (!strlen(key))
635                                                                 {
636                                                                         /* missing quote */
637                                                                         strcpy(result,"");
638                                                                         return 0;
639                                                                 }
640                                                                 key++;
641                                                         }
642                                                         key++;
643                                                         for (unsigned j = 0; j < strlen(key); j++)
644                                                         {
645                                                                 if (key[j] == '"')
646                                                                 {
647                                                                         key[j] = '\0';
648                                                                 }
649                                                         }
650                                                         strlcpy(result,key,MAXBUF);
651                                                         return 1;
652                                                 }
653                                         }
654                                 }
655                         }
656                         if (!strcmp(c_tag,tag))
657                         {
658                                 /* correct tag, but wrong index */
659                                 idx++;
660                         }
661                         c_tag[0] = '\0';
662                         buffer[0] = '\0';
663                         ptr = 0;
664                         tptr = 0;
665                 }
666                 if (c != '>')
667                 {
668                         if ((in_token) && (c != '\n') && (c != '\r'))
669                         {
670                                 buffer[ptr++] = c;
671                                 buffer[ptr] = '\0';
672                         }
673                 }
674         }
675         strcpy(result,""); // value or its tag not found at all
676         return 0;
677 }
678
679
680
681 int ConfValue(char* tag, char* var, int index, char *result,std::stringstream *config)
682 {
683         ReadConf(config, tag, var, index, result);
684         return 0;
685 }
686
687
688
689 // This will bind a socket to a port. It works for UDP/TCP
690 int BindSocket (int sockfd, struct sockaddr_in client, struct sockaddr_in server, int port, char* addr)
691 {
692         memset((char *)&server,0,sizeof(server));
693         struct in_addr addy;
694         inet_aton(addr,&addy);
695         server.sin_family = AF_INET;
696         if (!strcmp(addr,""))
697         {
698                 server.sin_addr.s_addr = htonl(INADDR_ANY);
699         }
700         else
701         {
702                 server.sin_addr = addy;
703         }
704         server.sin_port = htons(port);
705         if (bind(sockfd,(struct sockaddr*)&server,sizeof(server))<0)
706         {
707                 return(ERROR);
708         }
709         else
710         {
711                 listen(sockfd, MaxConn);
712                 return(TRUE);
713         }
714 }
715
716
717 // Open a TCP Socket
718 int OpenTCPSocket (void)
719 {
720         int sockfd;
721         int on = 1;
722         struct linger linger = { 0 };
723   
724         if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
725                 return (ERROR);
726         else
727         {
728                 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
729                 /* This is BSD compatible, setting l_onoff to 0 is *NOT* http://web.irc.org/mla/ircd-dev/msg02259.html */
730                 linger.l_onoff = 1;
731                 linger.l_linger = 1;
732                 setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (const char*)&linger,sizeof(linger));
733                 return (sockfd);
734         }
735 }