/[cvs]/eggdrop1.8/src/bg.c
ViewVC logotype

Contents of /eggdrop1.8/src/bg.c

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.2 - (show annotations) (download) (as text)
Mon Mar 14 03:03:28 2011 UTC (8 years ago) by thommey
Branch: MAIN
CVS Tags: HEAD
Changes since 1.1: +4 -4 lines
File MIME type: text/x-chdr
Moved variable declarations to conform to C89 (beginning of blocks only)

1 /*
2 * bg.c -- handles:
3 * moving the process to the background, i.e. forking, while keeping threads
4 * happy.
5 *
6 * $Id: bg.c,v 1.1.1.1 2010/07/26 21:11:06 simple Exp $
7 */
8 /*
9 * Copyright (C) 1997 Robey Pointer
10 * Copyright (C) 1999 - 2010 Eggheads Development Team
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 */
26
27 #include "main.h"
28 #include <signal.h>
29 #include "bg.h"
30
31 extern char pid_file[];
32
33 /* When threads are started during eggdrop's init phase, we can't simply
34 * fork() later on, because that only copies the VM space over and
35 * doesn't actually duplicate the threads.
36 *
37 * To work around this, we fork() very early and let the parent process
38 * wait in an event loop. As soon as the init phase is completed and we
39 * would normally move to the background, the child process simply
40 * messages it's parent that it may now quit. This allows us to control
41 * the terminal long enough to, e.g. properly feed error messages to
42 * cron scripts and let the user abort the loading process by hitting
43 * CTRL+C.
44 *
45 *
46 * [ Parent process ] [ Child process ]
47 *
48 * main()
49 * ...
50 * bg_prepare_split()
51 * fork() - created -
52 * waiting in event loop. continuing execution in main()
53 * ...
54 * completed init.
55 * bg_do_detach()
56 * message parent new PID file-
57 * name.
58 * receives new PID filename
59 * message.
60 * message parent to quit.
61 * receives quit message. continues to main loop.
62 * writes PID to file. ...
63 * exits.
64 */
65
66 /* Format of messages sent from the newly forked process to the
67 * original process, connected to the terminal.
68 */
69 typedef struct {
70 enum {
71 BG_COMM_QUIT, /* Quit original process. Write PID file, detach. */
72 BG_COMM_ABORT, /* Quit original process. */
73 BG_COMM_TRANSFERPF /* Sending pid_file. */
74 } comm_type;
75 union {
76 struct { /* Data for BG_COMM_TRANSFERPF. */
77 int len; /* Length of the file name. */
78 } transferpf;
79 } comm_data;
80 } bg_comm_t;
81
82 typedef enum {
83 BG_NONE = 0, /* No forking has taken place yet. */
84 BG_SPLIT, /* I'm the newly forked process. */
85 BG_PARENT /* I'm the original process. */
86 } bg_state_t;
87
88 typedef struct {
89 int comm_recv; /* Receives messages from the child process. */
90 int comm_send; /* Sends messages to the parent process. */
91 bg_state_t state; /* Current state, see above enum descriptions. */
92 pid_t child_pid; /* PID of split process. */
93 } bg_t;
94
95 static bg_t bg = { 0 };
96
97
98 /* Do everything we normally do after we have split off a new
99 * process to the background. This includes writing a PID file
100 * and informing the user of the split.
101 */
102 static void bg_do_detach(pid_t p)
103 {
104 FILE *fp;
105
106 /* Need to attempt to write pid now, not later. */
107 unlink(pid_file);
108 fp = fopen(pid_file, "w");
109 if (fp != NULL) {
110 fprintf(fp, "%u\n", p);
111 if (fflush(fp)) {
112 /* Kill bot incase a botchk is run from crond. */
113 printf(EGG_NOWRITE, pid_file);
114 printf(" Try freeing some disk space\n");
115 fclose(fp);
116 unlink(pid_file);
117 exit(1);
118 }
119 fclose(fp);
120 } else
121 printf(EGG_NOWRITE, pid_file);
122 printf("Launched into the background (pid: %d)\n\n", p);
123 #ifdef HAVE_SETPGID
124 setpgid(p, p);
125 #endif
126 exit(0);
127 }
128
129 void bg_prepare_split(void)
130 {
131 pid_t p;
132 bg_comm_t message;
133
134 if (!fork_before_tcl())
135 return;
136
137 /* Create a pipe between parent and split process, fork to create a
138 * parent and a split process and wait for messages on the pipe. */
139 {
140 int comm_pair[2];
141
142 if (pipe(comm_pair) < 0)
143 fatal("CANNOT OPEN PIPE.", 0);
144
145 bg.comm_recv = comm_pair[0];
146 bg.comm_send = comm_pair[1];
147 }
148
149 p = fork();
150 if (p == -1)
151 fatal("CANNOT FORK PROCESS.", 0);
152 if (p == 0) {
153 bg.state = BG_SPLIT;
154 return;
155 } else {
156 bg.child_pid = p;
157 bg.state = BG_PARENT;
158 }
159
160 while (read(bg.comm_recv, &message, sizeof(message)) > 0) {
161 switch (message.comm_type) {
162 case BG_COMM_QUIT:
163 bg_do_detach(p);
164 break;
165 case BG_COMM_ABORT:
166 exit(1);
167 break;
168 case BG_COMM_TRANSFERPF:
169 /* Now transferring file from split process.
170 */
171 if (message.comm_data.transferpf.len >= 40)
172 message.comm_data.transferpf.len = 40 - 1;
173 /* Next message contains data. */
174 if (read(bg.comm_recv, pid_file, message.comm_data.transferpf.len) <= 0)
175 goto error;
176 pid_file[message.comm_data.transferpf.len] = 0;
177 break;
178 }
179 }
180
181 error:
182 /* We only reach this point in case of an error.
183 */
184 fatal("COMMUNICATION THROUGH PIPE BROKE.", 0);
185 }
186
187 /* Transfer contents of pid_file to parent process. This is necessary,
188 * as the pid_file[] buffer has changed in this fork by now, but the
189 * parent needs an up-to-date version.
190 */
191 static void bg_send_pidfile(void)
192 {
193 bg_comm_t message;
194
195 message.comm_type = BG_COMM_TRANSFERPF;
196 message.comm_data.transferpf.len = strlen(pid_file);
197
198 /* Send type message. */
199 if (write(bg.comm_send, &message, sizeof(message)) < 0)
200 goto error;
201 /* Send data. */
202 if (write(bg.comm_send, pid_file, message.comm_data.transferpf.len) < 0)
203 goto error;
204 return;
205 error:
206 fatal("COMMUNICATION THROUGH PIPE BROKE.", 0);
207 }
208
209 void bg_send_quit(bg_quit_t q)
210 {
211 if (!fork_before_tcl())
212 return;
213
214 if (bg.state == BG_PARENT) {
215 kill(bg.child_pid, SIGKILL);
216 } else if (bg.state == BG_SPLIT) {
217 bg_comm_t message;
218
219 if (q == BG_QUIT) {
220 bg_send_pidfile();
221 message.comm_type = BG_COMM_QUIT;
222 } else
223 message.comm_type = BG_COMM_ABORT;
224 /* Send message. */
225 if (write(bg.comm_send, &message, sizeof(message)) < 0)
226 fatal("COMMUNICATION THROUGH PIPE BROKE.", 0);
227 }
228 }
229
230 void bg_do_split(void)
231 {
232 if (fork_before_tcl()) {
233 /* Tell our parent process to go away now, as we don't need it anymore. */
234 bg_send_quit(BG_QUIT);
235
236 } else {
237 /* Split off a new process. */
238 int xx = fork();
239
240 if (xx == -1)
241 fatal("CANNOT FORK PROCESS.", 0);
242 if (xx != 0)
243 bg_do_detach(xx);
244 }
245 }

webmaster@eggheads.org
ViewVC Help
Powered by ViewVC 1.1.23