1 /********************************************************************\
2 * BitlBee -- An IRC to other IM-networks gateway *
3 * *
4 * Copyright 2002-2004 Wilmer van der Gaast and others *
5 \********************************************************************/
6
7 /* Main file */
8
9 /*
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License with
21 the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
22 if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
23 Fifth Floor, Boston, MA 02110-1301 USA
24 */
25
26 #define BITLBEE_CORE
27 #include "bitlbee.h"
28 #include "commands.h"
29 #include "protocols/nogaim.h"
30 #include "help.h"
31 #include "ipc.h"
32 #include <signal.h>
33 #include <stdio.h>
34 #include <errno.h>
35
36 static gboolean bitlbee_io_new_client(gpointer data, gint fd, b_input_condition condition);
37
try_listen(struct addrinfo * res)38 static gboolean try_listen(struct addrinfo *res)
39 {
40 int i;
41
42 global.listen_socket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
43 if (global.listen_socket < 0) {
44 log_error("socket");
45 return FALSE;
46 }
47
48 #ifdef IPV6_V6ONLY
49 if (res->ai_family == AF_INET6) {
50 i = 0;
51 setsockopt(global.listen_socket, IPPROTO_IPV6, IPV6_V6ONLY,
52 (char *) &i, sizeof(i));
53 }
54 #endif
55
56 /* TIME_WAIT (?) sucks.. */
57 i = 1;
58 setsockopt(global.listen_socket, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
59
60 i = bind(global.listen_socket, res->ai_addr, res->ai_addrlen);
61 if (i == -1) {
62 closesocket(global.listen_socket);
63 global.listen_socket = -1;
64
65 log_error("bind");
66 return FALSE;
67 }
68
69 return TRUE;
70 }
71
bitlbee_daemon_init()72 int bitlbee_daemon_init()
73 {
74 struct addrinfo *res, hints, *addrinfo_bind;
75 int i;
76 FILE *fp;
77
78 log_link(LOGLVL_ERROR, LOGOUTPUT_CONSOLE);
79 log_link(LOGLVL_WARNING, LOGOUTPUT_CONSOLE);
80
81 memset(&hints, 0, sizeof(hints));
82 hints.ai_family = PF_UNSPEC;
83 hints.ai_socktype = SOCK_STREAM;
84 hints.ai_flags = AI_PASSIVE
85 #ifdef AI_ADDRCONFIG
86 /* Disabled as it may be doing more harm than good: this flag
87 ignores IPv6 addresses on lo (which seems reasonable), but
88 the result is that some clients (including irssi) try to
89 connect to ::1 and fail.
90 | AI_ADDRCONFIG */
91 #endif
92 ;
93
94 i = getaddrinfo(global.conf->iface_in, global.conf->port, &hints, &addrinfo_bind);
95 if (i) {
96 log_message(LOGLVL_ERROR, "Couldn't parse address `%s': %s",
97 global.conf->iface_in, gai_strerror(i));
98 return -1;
99 }
100
101 global.listen_socket = -1;
102
103 /* Try IPv6 first (which will become an IPv6+IPv4 socket). */
104 for (res = addrinfo_bind; res; res = res->ai_next) {
105 if (res->ai_family == AF_INET6 && try_listen(res)) {
106 break;
107 }
108 }
109
110 /* The rest (so IPv4, I guess). */
111 if (res == NULL) {
112 for (res = addrinfo_bind; res; res = res->ai_next) {
113 if (res->ai_family != AF_INET6 && try_listen(res)) {
114 break;
115 }
116 }
117 }
118
119 freeaddrinfo(addrinfo_bind);
120
121 if (global.listen_socket == -1 && errno == EADDRINUSE) {
122 log_message(LOGLVL_ERROR, "Can't listen on port %s. Is another bitlbee already running?",
123 global.conf->port);
124 return(-1);
125 }
126
127 i = listen(global.listen_socket, 10);
128 if (i == -1) {
129 log_error("listen");
130 return(-1);
131 }
132
133 global.listen_watch_source_id = b_input_add(global.listen_socket, B_EV_IO_READ, bitlbee_io_new_client, NULL);
134
135 if (!global.conf->nofork) {
136 i = fork();
137 if (i == -1) {
138 log_error("fork");
139 return(-1);
140 } else if (i != 0) {
141 exit(0);
142 }
143
144 setsid();
145 i = chdir("/");
146 /* Don't use i, just make gcc happy. :-/ */
147
148 if (getenv("_BITLBEE_RESTART_STATE") == NULL) {
149 for (i = 0; i < 3; i++) {
150 if (close(i) == 0) {
151 /* Keep something bogus on those fd's just in case. */
152 open("/dev/null", O_WRONLY);
153 }
154 }
155 }
156 }
157
158 if (global.conf->runmode == RUNMODE_FORKDAEMON) {
159 ipc_master_load_state(getenv("_BITLBEE_RESTART_STATE"));
160 }
161
162 if (global.conf->runmode == RUNMODE_DAEMON || global.conf->runmode == RUNMODE_FORKDAEMON) {
163 ipc_master_listen_socket();
164 }
165
166 if ((fp = fopen(global.conf->pidfile, "w"))) {
167 fprintf(fp, "%d\n", (int) getpid());
168 fclose(fp);
169 } else {
170 log_message(LOGLVL_WARNING, "Warning: Couldn't write PID to `%s'", global.conf->pidfile);
171 }
172
173 if (!global.conf->nofork) {
174 log_link(LOGLVL_ERROR, LOGOUTPUT_SYSLOG);
175 log_link(LOGLVL_WARNING, LOGOUTPUT_SYSLOG);
176 }
177
178 return(0);
179 }
180
bitlbee_inetd_init()181 int bitlbee_inetd_init()
182 {
183 if (!irc_new(0)) {
184 return(1);
185 }
186
187 return(0);
188 }
189
bitlbee_io_current_client_read(gpointer data,gint fd,b_input_condition cond)190 gboolean bitlbee_io_current_client_read(gpointer data, gint fd, b_input_condition cond)
191 {
192 irc_t *irc = data;
193 char line[513];
194 int st;
195
196 st = read(irc->fd, line, sizeof(line) - 1);
197 if (st == 0) {
198 irc_abort(irc, 1, "Connection reset by peer");
199 return FALSE;
200 } else if (st < 0) {
201 if (sockerr_again()) {
202 return TRUE;
203 } else {
204 irc_abort(irc, 1, "Read error: %s", strerror(errno));
205 return FALSE;
206 }
207 }
208
209 line[st] = '\0';
210 if (irc->readbuffer == NULL) {
211 irc->readbuffer = g_strdup(line);
212 } else {
213 irc->readbuffer = g_renew(char, irc->readbuffer, strlen(irc->readbuffer) + strlen(line) + 1);
214 strcpy((irc->readbuffer + strlen(irc->readbuffer)), line);
215 }
216
217 irc_process(irc);
218
219 /* Normally, irc_process() shouldn't call irc_free() but irc_abort(). Just in case: */
220 if (!g_slist_find(irc_connection_list, irc)) {
221 log_message(LOGLVL_WARNING, "Abnormal termination of connection with fd %d.", fd);
222 return FALSE;
223 }
224
225 /* Very naughty, go read the RFCs! >:) */
226 if (irc->readbuffer && (strlen(irc->readbuffer) > 1024)) {
227 irc_abort(irc, 0, "Maximum line length exceeded");
228 return FALSE;
229 }
230
231 return TRUE;
232 }
233
bitlbee_io_current_client_write(gpointer data,gint fd,b_input_condition cond)234 gboolean bitlbee_io_current_client_write(gpointer data, gint fd, b_input_condition cond)
235 {
236 irc_t *irc = data;
237 int st, size;
238
239 size = irc->sendbuffer->len;
240
241 if (size == 0) {
242 return FALSE;
243 }
244
245 st = write(irc->fd, irc->sendbuffer->str, size);
246
247 if (st == 0 || (st < 0 && !sockerr_again())) {
248 irc_abort(irc, 1, "Write error: %s", strerror(errno));
249 return FALSE;
250 } else if (st < 0) { /* && sockerr_again() */
251 return TRUE;
252 }
253
254 if (st == size) {
255 g_string_truncate(irc->sendbuffer, 0);
256 irc->w_watch_source_id = 0;
257
258 return FALSE;
259 } else {
260 g_string_erase(irc->sendbuffer, 0, st);
261
262 return TRUE;
263 }
264 }
265
bitlbee_io_new_client(gpointer data,gint fd,b_input_condition condition)266 static gboolean bitlbee_io_new_client(gpointer data, gint fd, b_input_condition condition)
267 {
268 socklen_t size = sizeof(struct sockaddr_in);
269 struct sockaddr_in conn_info;
270 int new_socket = accept(global.listen_socket, (struct sockaddr *) &conn_info, &size);
271
272 if (new_socket == -1) {
273 log_message(LOGLVL_WARNING, "Could not accept new connection: %s", strerror(errno));
274 return TRUE;
275 }
276
277 if (global.conf->runmode == RUNMODE_FORKDAEMON) {
278 pid_t client_pid = 0;
279 int fds[2];
280
281 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == -1) {
282 log_message(LOGLVL_WARNING, "Could not create IPC socket for client: %s", strerror(errno));
283 fds[0] = fds[1] = -1;
284 }
285
286 sock_make_nonblocking(fds[0]);
287 sock_make_nonblocking(fds[1]);
288
289 client_pid = fork();
290
291 if (client_pid > 0 && fds[0] != -1) {
292 struct bitlbee_child *child;
293
294 /* TODO: Stuff like this belongs in ipc.c. */
295 child = g_new0(struct bitlbee_child, 1);
296 child->pid = client_pid;
297 child->ipc_fd = fds[0];
298 child->ipc_inpa = b_input_add(child->ipc_fd, B_EV_IO_READ, ipc_master_read, child);
299 child->to_fd = -1;
300 child_list = g_slist_append(child_list, child);
301
302 log_message(LOGLVL_INFO, "Creating new subprocess with pid %d.", (int) client_pid);
303
304 /* Close some things we don't need in the parent process. */
305 close(new_socket);
306 close(fds[1]);
307 } else if (client_pid == 0) {
308 irc_t *irc;
309
310 b_main_init();
311
312 /* Close the listening socket, we're a client. */
313 close(global.listen_socket);
314 b_event_remove(global.listen_watch_source_id);
315
316 /* Make a new pipe for the shutdown signal handler */
317 sighandler_shutdown_setup();
318
319 /* Make the connection. */
320 irc = irc_new(new_socket);
321
322 /* We can store the IPC fd there now. */
323 global.listen_socket = fds[1];
324 global.listen_watch_source_id = b_input_add(fds[1], B_EV_IO_READ, ipc_child_read, irc);
325
326 close(fds[0]);
327
328 ipc_master_free_all();
329 }
330 } else {
331 log_message(LOGLVL_INFO, "Creating new connection with fd %d.", new_socket);
332 irc_new(new_socket);
333 }
334
335 return TRUE;
336 }
337
bitlbee_shutdown(gpointer data,gint fd,b_input_condition cond)338 gboolean bitlbee_shutdown(gpointer data, gint fd, b_input_condition cond)
339 {
340 /* Send the message here with now=TRUE to ensure it arrives */
341 irc_write_all(TRUE, "ERROR :Closing link: BitlBee server shutting down");
342
343 /* Try to save data for all active connections (if desired). */
344 while (irc_connection_list != NULL) {
345 irc_abort(irc_connection_list->data, TRUE, NULL);
346 }
347
348 /* We'll only reach this point when not running in inetd mode: */
349 b_main_quit();
350
351 return FALSE;
352 }
353