1 /*
2 * apcupsd.c
3 *
4 * APC UPS monitoring daemon.
5 */
6
7 /*
8 * Copyright (C) 1999-2005 Kern Sibbald
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of version 2 of the GNU General
12 * Public License as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public
20 * License along with this program; if not, write to the Free
21 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22 * MA 02110-1335, USA.
23 */
24
25 #include "apc.h"
26
27 UPSINFO *core_ups = NULL;
28
29 static void daemon_start(void);
30
31 int pidcreated = 0;
32 extern int kill_on_powerfail;
33 extern FILE *trace_fd;
34 extern char *pidfile;
35
36 /*
37 * The terminate function and trapping signals allows apcupsd
38 * to exit and cleanly close logfiles, and reset the tty back
39 * to its original settings. You may want to add this
40 * to all configurations. Of course, the file descriptors
41 * must be global for this to work, I also set them to
42 * NULL initially to allow terminate to determine whether
43 * it should close them.
44 */
apcupsd_terminate(int sig)45 void apcupsd_terminate(int sig)
46 {
47 UPSINFO *ups = core_ups;
48
49 if (sig != 0)
50 log_event(ups, LOG_WARNING, "apcupsd exiting, signal %u", sig);
51
52 clean_threads();
53 clear_files();
54 if (ups->driver)
55 device_close(ups);
56 delete_lockfile(ups);
57 if (pidcreated)
58 unlink(pidfile);
59 log_event(ups, LOG_NOTICE, "apcupsd shutdown succeeded");
60 destroy_ups(ups);
61 closelog();
62 _exit(0);
63 }
64
apcupsd_error_cleanup(UPSINFO * ups)65 void apcupsd_error_cleanup(UPSINFO *ups)
66 {
67 if (ups->driver)
68 device_close(ups);
69 delete_lockfile(ups);
70 if (pidcreated)
71 unlink(pidfile);
72 clean_threads();
73 log_event(ups, LOG_ERR, "apcupsd error shutdown completed");
74 destroy_ups(ups);
75 closelog();
76 exit(1);
77 }
78
79 /*
80 * Subroutine error_out prints FATAL ERROR with file,
81 * line number, and the error message then cleans up
82 * and exits. It is normally called from the Error_abort
83 * define, which inserts the file and line number.
84 */
apcupsd_error_out(const char * file,int line,const char * fmt,va_list arg_ptr)85 void apcupsd_error_out(const char *file, int line, const char *fmt, va_list arg_ptr)
86 {
87 char buf[256];
88 int i;
89
90 asnprintf(buf, sizeof(buf),
91 "apcupsd FATAL ERROR in %s at line %d\n", file, line);
92
93 i = strlen(buf);
94 avsnprintf((char *)&buf[i], sizeof(buf) - i, (char *)fmt, arg_ptr);
95
96 fprintf(stderr, "%s", buf);
97 log_event(core_ups, LOG_ERR, "%s", buf);
98 apcupsd_error_cleanup(core_ups); /* finish the work */
99 }
100
101 /*
102 * ApcupsdMain is called from win32/winmain.cpp
103 * we need to eliminate "main" as an entry point,
104 * otherwise, it interferes with the Windows
105 * startup.
106 */
107 #ifdef HAVE_MINGW
108 # define main ApcupsdMain
109 #endif
110
111 /* Main program */
main(int argc,char * argv[])112 int main(int argc, char *argv[])
113 {
114 UPSINFO *ups;
115 int tmp_fd, i;
116
117 /* Set specific error_* handlers. */
118 error_out = apcupsd_error_out;
119
120 /*
121 * Default config file. If we set a config file in startup switches, it
122 * will be re-filled by parse_options()
123 */
124 cfgfile = APCCONF;
125
126 /*
127 * Create fake stdout in order to circumvent problems
128 * which occur if apcupsd doesn't have a valid stdout
129 * Fix by Alexander Schremmer <alex at alexanderweb dot de>
130 */
131 tmp_fd = open("/dev/null", O_RDONLY);
132 if (tmp_fd > 2) {
133 close(tmp_fd);
134 } else if (tmp_fd >= 0) {
135 for (i = 1; tmp_fd + i <= 2; i++)
136 dup2(tmp_fd, tmp_fd + i);
137 }
138
139 /*
140 * If there's not one in libc, then we have to use our own version
141 * which requires initialization.
142 */
143 strlcpy(argvalue, argv[0], sizeof(argvalue));
144
145 ups = new_ups(); /* get new ups */
146 if (!ups)
147 Error_abort("%s: init_ipc failed.\n", argv[0]);
148
149 init_ups_struct(ups);
150 core_ups = ups; /* this is our core ups structure */
151
152 /* parse_options is self messaging on errors, so we need only to exit() */
153 if (parse_options(argc, argv))
154 exit(1);
155
156 Dmsg(10, "Options parsed.\n");
157
158 if (show_version) {
159 printf("apcupsd " APCUPSD_RELEASE " (" ADATE ") " APCUPSD_HOST "\n");
160 exit(0);
161 }
162
163 /*
164 * We open the log file early to be able to write to it
165 * it at any time. However, please note that if the user
166 * specifies a different facility in the config file
167 * the log will be closed and reopened (see match_facility())
168 * in apcconfig.c. Any changes here should also be made
169 * to the code in match_facility().
170 */
171 openlog("apcupsd", LOG_CONS | LOG_PID, ups->sysfac);
172
173 #ifndef DEBUG
174 if ((getuid() != 0) && (geteuid() != 0))
175 Error_abort("Needs super user privileges to run.\n");
176 #endif
177
178 check_for_config(ups, cfgfile);
179 Dmsg(10, "Config file %s processed.\n", cfgfile);
180
181 /*
182 * Disallow --kill-on-powerfail in conjunction with simple signaling
183 * UPSes. Such UPSes have no shutdown grace period so using --kill-on-
184 * powerfail would guarantee an unclean shutdown.
185 */
186 if (kill_on_powerfail && ups->mode.type == DUMB_UPS) {
187 kill_on_powerfail = 0;
188 log_event(ups, LOG_WARNING,
189 "Ignoring --kill-on-powerfail since it is unsafe "
190 "on Simple Signaling UPSes");
191 }
192
193 /* Attach the correct driver. */
194 attach_driver(ups);
195 if (ups->driver == NULL)
196 Error_abort("Apcupsd cannot continue without a valid driver.\n");
197
198 ups->start_time = time(NULL);
199
200 if (!hibernate_ups && !shutdown_ups && go_background) {
201 daemon_start();
202
203 /* Reopen log file, closed during becoming daemon */
204 openlog("apcupsd", LOG_CONS | LOG_PID, ups->sysfac);
205 }
206
207 init_signals(apcupsd_terminate);
208
209 /* Create temp events file if we are not doing a hibernate or shutdown */
210 if (!hibernate_ups && !shutdown_ups && ups->eventfile[0] != 0) {
211 ups->event_fd = open(ups->eventfile, O_RDWR | O_CREAT | O_APPEND | O_CLOEXEC, 0644);
212 if (ups->event_fd < 0) {
213 log_event(ups, LOG_WARNING, "Could not open events file %s: %s\n",
214 ups->eventfile, strerror(errno));
215 }
216 }
217
218 if (create_lockfile(ups) == LCKERROR) {
219 Error_abort("Unable to create UPS lock file.\n"
220 " If apcupsd or apctest is already running,\n"
221 " please stop it and run this program again.\n");
222 }
223
224 make_pid_file();
225 pidcreated = 1;
226
227 if (hibernate_ups || shutdown_ups)
228 {
229 // If we're hibernating or shutting down the UPS, setup is a one-shot.
230 // If it fails, we're toast; no retries
231 if (!setup_device(ups))
232 Error_abort("Unable to open UPS device for hibernate or shutdown");
233
234 if (hibernate_ups)
235 initiate_hibernate(ups);
236 else
237 initiate_shutdown(ups);
238
239 apcupsd_terminate(0);
240 }
241
242 /*
243 * From now ... we must _only_ start up threads!
244 * No more unchecked writes to UPSINFO because the threads must rely
245 * on write locks and up to date data in the shared structure.
246 */
247
248 /* Network status information server */
249 if (ups->netstats) {
250 start_thread(ups, do_server, "apcnis", argv[0]);
251 Dmsg(10, "NIS thread started.\n");
252 }
253
254 log_event(ups, LOG_NOTICE,
255 "apcupsd " APCUPSD_RELEASE " (" ADATE ") " APCUPSD_HOST " startup succeeded");
256
257 /* main processing loop */
258 do_device(ups);
259
260 apcupsd_terminate(0);
261 return 0; /* to keep compiler happy */
262 }
263
264 extern int debug_level;
265
266 /*
267 * Initialize a daemon process completely detaching us from
268 * any terminal processes.
269 */
daemon_start(void)270 static void daemon_start(void)
271 {
272 #if !defined(HAVE_MINGW)
273 int i, fd;
274 pid_t cpid;
275 mode_t oldmask;
276
277 #ifndef HAVE_QNX_OS
278 if ((cpid = fork()) < 0)
279 Error_abort("Cannot fork to become daemon\n");
280 else if (cpid > 0)
281 exit(0); /* parent exits */
282
283 /* Child continues */
284 setsid(); /* become session leader */
285 #else
286 if (procmgr_daemon(EXIT_SUCCESS, PROCMGR_DAEMON_NOCHDIR
287 | PROCMGR_DAEMON_NOCLOSE
288 | PROCMGR_DAEMON_NODEVNULL
289 | PROCMGR_DAEMON_KEEPUMASK) == -1)
290 {
291 Error_abort("Couldn't become daemon\n");
292 }
293 #endif /* HAVE_QNX_OS */
294
295 /* Call closelog() to close syslog file descriptor */
296 closelog();
297
298 /*
299 * In the PRODUCTION system, we close ALL file descriptors unless
300 * debugging is on then we leave stdin, stdout, and stderr open.
301 * We also take care to leave the trace fd open if tracing is on.
302 * Furthermore, if tracing we redirect stdout and stderr to the
303 * trace log.
304 */
305 for (i=0; i<sysconf(_SC_OPEN_MAX); i++) {
306 if (debug_level && i == STDIN_FILENO)
307 continue;
308 if (trace_fd && i == fileno(trace_fd))
309 continue;
310 if (debug_level && (i == STDOUT_FILENO || i == STDERR_FILENO)) {
311 if (trace_fd)
312 dup2(fileno(trace_fd), i);
313 continue;
314 }
315 close(i);
316 }
317
318 /* move to root directory */
319 chdir("/");
320
321 /*
322 * Avoid creating files 0666 but don't override a
323 * more restrictive umask set by the caller.
324 */
325 oldmask = umask(022);
326 oldmask |= 022;
327 umask(oldmask);
328
329 /*
330 * Make sure we have fd's 0, 1, 2 open
331 * If we don't do this one of our sockets may open
332 * there and if we then use stdout, it could
333 * send total garbage to our socket.
334 */
335 fd = open("/dev/null", O_RDONLY);
336 if (fd > 2) {
337 close(fd);
338 } else if (fd >= 0) {
339 for (i = 1; fd + i <= 2; i++)
340 dup2(fd, fd + i);
341 }
342 #endif /* HAVE_MINGW */
343 }
344