107d50eef806d9b8f31f816c9a9bd10ec24f8a27
[NeonServV5.git] / src / main.c
1 /* main.c - NeonServ v5.3
2  * Copyright (C) 2011-2012  Philipp Kreil (pk910)
3  * 
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License 
15  * along with this program. If not, see <http://www.gnu.org/licenses/>. 
16  */
17
18 #include "main.h"
19 #include "ClientSocket.h"
20 #include "UserNode.h"
21 #include "ChanNode.h"
22 #include "IRCEvents.h"
23 #include "IRCParser.h"
24 #include "modcmd.h"
25 #include "WHOHandler.h"
26 #include "bots.h"
27 #include "mysqlConn.h"
28 #include "HandleInfoHandler.h"
29 #include "lang.h"
30 #include "tools.h"
31 #include "timeq.h"
32 #include "EventLogger.h"
33 #include "ModeNode.h"
34 #include "IRCQueue.h"
35 #include "DBHelper.h"
36 #include "commands.h"
37 #include "ConfigParser.h"
38 #include "ssl.h"
39
40 time_t start_time;
41 static int running, hard_restart;
42 static int statistics_requested_lusers = 0;
43 int statistics_enabled;
44 TIMEQ_CALLBACK(main_statistics);
45 #ifdef HAVE_THREADS
46 int running_threads;
47 pthread_mutex_t cache_sync;
48 pthread_mutex_t whohandler_sync, whohandler_mass_sync;
49 #endif
50
51 void cleanup() {
52     free_sockets();
53     free_parser();
54     free_UserNode();
55     free_ChanNode();
56     free_bind();
57     free_modcmd();
58     free_whoqueue();
59     free_bots();
60     free_mysql();
61     free_handleinfohandler();
62     free_lang();
63 }
64
65 static int load_mysql_config() {
66     char *mysql_host, *mysql_user, *mysql_pass, *mysql_base;
67     int mysql_serverport;
68     if(loadConfig("neonserv.conf")) {
69         mysql_host = get_string_field("MySQL.host");
70         if(!mysql_host) {
71             perror("invalid neonserv.conf: missing MySQL.host");
72             return 0;
73         }
74         mysql_serverport = get_int_field("MySQL.port");
75         if(!mysql_serverport)
76             mysql_serverport = 3306;
77         mysql_user = get_string_field("MySQL.user");
78         if(!mysql_user) {
79             perror("invalid neonserv.conf: missing MySQL.user");
80             return 0;
81         }
82         mysql_pass = get_string_field("MySQL.pass");
83         if(!mysql_pass) {
84             perror("invalid neonserv.conf: missing MySQL.pass");
85             return 0;
86         }
87         mysql_base = get_string_field("MySQL.base");
88         if(!mysql_base) {
89             perror("invalid neonserv.conf: missing MySQL base");
90             return 0;
91         }
92     } else {
93         perror("Unable to load neonserv.conf");
94         return 0;
95     }
96     init_mysql(mysql_host, mysql_serverport, mysql_user, mysql_pass, mysql_base);
97     return 1;
98 }
99
100 #ifdef HAVE_THREADS
101 void * thread_main(void *arg) {
102     time_t socket_wait;
103     while(running) {
104         socket_wait = time(0) + SOCKET_SELECT_TIME;
105         do {
106             socket_loop(SOCKET_SELECT_TIME);
107         } while(time(0) < socket_wait);
108         clearTempUsers();
109         destroyEvents();
110     }
111     running_threads--;
112     return NULL;
113 }
114 #endif
115
116 int main(int argc, char *argv[]) {
117 main:
118     
119     start_time = time(0);
120     
121     #ifdef WIN32
122     int res;
123     WSADATA wsadata;
124     // Start Windows Sockets.
125     res = WSAStartup(MAKEWORD(2, 0), &wsadata);
126     if (res)
127     {
128         perror("Unable to start Windows Sockets");
129         return 0;
130     }
131     #endif
132     
133     if(!load_mysql_config()) return 0;
134     
135     statistics_enabled = get_int_field("statistics.enable");
136     
137     #ifdef HAVE_THREADS
138     THREAD_MUTEX_INIT(cache_sync);
139     THREAD_MUTEX_INIT(whohandler_sync);
140     THREAD_MUTEX_INIT(whohandler_mass_sync);
141     #endif
142     
143     queue_init();
144     init_sockets();
145     init_timeq();
146     init_lang();
147     ssl_init();
148     init_parser();
149     init_UserNode();
150     init_ChanNode();
151     init_ModeNode();
152     init_bind();
153         init_modcmd();
154     init_handleinfohandler();
155     init_tools();
156     register_commands();
157     init_bots();
158     init_DBHelper();
159     
160     load_languages();
161     int update_minutes = get_int_field("statistics.frequency");
162     if(!update_minutes) update_minutes = 2;
163     timeq_add(update_minutes * 60 + 10, main_statistics, NULL);
164     
165     int worker_threads = get_int_field("General.worker_threads");
166     if(!worker_threads) worker_threads = 1;
167     
168     running = 1;
169     #ifdef HAVE_THREADS
170     pthread_t tid[worker_threads];
171     int tid_id = 0;
172     for(tid_id = 0; tid_id < worker_threads; tid_id++) {
173         running_threads++;
174         pthread_create(&tid[tid_id], NULL, thread_main, NULL);
175     }
176     int usleep_delay = 1000000 / TICKS_PER_SECOND;
177     while(running) {
178         timeq_tick();
179         loop_bots();
180         queue_loop();
181         usleep(usleep_delay);
182     }
183     for(tid_id = 0; tid_id < worker_threads; tid_id++) {
184         pthread_join(tid[tid_id], NULL);
185     }
186     running_threads = 0;
187     #else
188     time_t socket_wait;
189     while(running) {
190         socket_wait = time(0) + SOCKET_SELECT_TIME;
191         do {
192             socket_loop(SOCKET_SELECT_TIME);
193         } while(time(0) < socket_wait);
194         timeq_tick();
195         loop_bots();
196         clearTempUsers();
197         destroyEvents();
198         queue_loop();
199     }
200     #endif
201     cleanup();
202     if(hard_restart) {
203         /* Append a NULL to the end of argv[]. */
204         char **restart_argv = (char **)alloca((argc + 1) * sizeof(char *));
205         memcpy(restart_argv, argv, argc * sizeof(char *));
206         restart_argv[argc] = NULL;
207         
208         #ifdef WIN32
209         execv(argv[0], (const char * const*)restart_argv);
210         #else
211         execv(argv[0], restart_argv);
212         #endif
213     }
214     goto main;
215 }
216
217 int stricmp (const char *s1, const char *s2)
218 {
219    if (s1 == NULL) return s2 == NULL ? 0 : -(*s2);
220    if (s2 == NULL) return *s1;
221    char c1, c2;
222    while ((c1 = tolower (*s1)) == (c2 = tolower (*s2)))
223    {
224      if (*s1 == '\0') break;
225      ++s1; ++s2;
226    }
227    return c1 - c2;
228 }
229
230 int stricmplen (const char *s1, const char *s2, int len)
231 {
232    if (s1 == NULL) return s2 == NULL ? 0 : -(*s2);
233    if (s2 == NULL) return *s1;
234    char c1, c2;
235    int i = 0;
236    while ((c1 = tolower (*s1)) == (c2 = tolower (*s2)))
237    {
238      i++;
239      if (*s1 == '\0') break;
240      ++s1; ++s2;
241      if(i == len) break;
242    }
243    return c1 - c2;
244 }
245
246 void restart_bot(int do_hard_restart) {
247     hard_restart = do_hard_restart;
248     running = 0;
249 }
250
251 void stop_bot() {
252     cleanup();
253     exit(0);
254 }
255
256 void reload_config() {
257     loadConfig("neonserv.conf");
258 }
259
260 TIMEQ_CALLBACK(main_statistics) {
261     int update_minutes = get_int_field("statistics.frequency");
262     if(!update_minutes) update_minutes = 2;
263     timeq_add(update_minutes * 60, main_statistics, NULL);
264     if(get_int_field("statistics.enable")) {
265         statistics_enabled = 1;
266         statistics_requested_lusers = 1;
267         if(get_int_field("statistics.include_lusers")) {
268             struct ClientSocket *bot, *lusersbot = NULL;
269             for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) {
270                 if(bot->flags & SOCKET_FLAG_PREFERRED)
271                     lusersbot = bot;
272             }
273             bot = lusersbot;
274             if(bot == NULL) bot = getBots(SOCKET_FLAG_READY, NULL);
275             putsock(bot, "LUSERS");
276         } else {
277             statistics_update();
278         }
279     } else
280         statistics_enabled = 0;
281 }
282
283 void statistics_update() {
284     if(get_int_field("statistics.enable") && statistics_requested_lusers && get_string_field("statistics.execute")) {
285         statistics_requested_lusers = 0;
286         char command[MAXLEN];
287         /* parameters:
288          - visible users
289          - visible chanusers
290          - visible channels
291          - privmsg per minute
292          - commands per minute
293          - network users
294          - network channels
295         */
296         sprintf(command, "%s %d %d %d %d %d %d %d", get_string_field("statistics.execute"), getUserCount(), getChanUserCount(), getChannelCount(), statistics_privmsg, statistics_commands, statistics_network_users, statistics_network_channels);
297         statistics_privmsg = 0;
298         statistics_commands = 0;
299         system(command);
300     }
301 }