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