2 * Copyright (c) 2003, 2005 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Liam J. Foy <liamfoy@dragonflybsd.org>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * $DragonFly: src/usr.sbin/battd/battd.c,v 1.8 2005/03/09 18:58:22 liamfoy Exp $
38 #include <sys/ioctl.h>
39 #include <sys/types.h>
42 #include <machine/apm_bios.h>
53 #define APMUNKNOWN 255 /* Unknown value. */
54 #define AC_LINE_IN 1 /* AC Line Status values. */
57 #define APM_DEVICE "/dev/apm" /* Default APM Device. */
58 #define ERR_OUT 1 /* Values for error writing. */
60 #define DEFAULT_ALERT 10 /* Default alert is 10%. */
63 int alert_per; /* Percentage to alert user on */
64 int alert_time; /* User can also alert when there is only X amount of minutes left */
65 int alert_status; /* Alert when battery is either high, low, critical */
66 const char *apm_dev; /* APM Device */
67 const char *exec_cmd; /* Command to execute if desired */
74 static int check_percent(int);
75 static int check_stat(int);
76 static int check_time(int);
77 static int get_apm_info(struct apm_info *, int, const char *, int);
78 static void execute_cmd(const char *, int *);
79 static void write_emerg(const char *, int *);
80 static void usage(void); __dead2
86 fprintf(stderr, "usage: battd [-dEhT] [-p percent] [-t minutes] [-s status]\n"
87 " [-f device] [-e command] [-c seconds]\n");
89 fprintf(stderr, "usage: battd [-EhT] [-p percent] [-t minutes] [-s status]\n"
90 " [-f device] [-e command] [-c seconds]\n");
96 check_percent(int apm_per)
98 if (apm_per < 0 || apm_per >= APMUNKNOWN || apm_per > 100)
105 check_time(int apm_time)
114 check_stat(int apm_stat)
116 if (apm_stat > 3 || apm_stat < 0)
122 /* Fetch battery information */
124 get_apm_info(struct apm_info *ai, int fp_dev, const char *apm_dev, int err_to)
126 if (ioctl(fp_dev, APMIO_GETINFO, ai) == -1) {
128 err(1, "ioctl(APMIO_GETINFO) device: %s", apm_dev);
130 syslog(LOG_ERR, "ioctl(APMIO_GETINFO) device: %s: %m ",
139 /* Execute command. */
141 execute_cmd(const char *exec_cmd, int *exec_cont)
146 if (exec_cmd != NULL) {
147 if ((pid = fork()) == -1) {
148 /* Here fork failed */
154 syslog(LOG_ERR, "fork failed: %m");
155 } else if (pid == 0) {
156 execl("/bin/sh", "/bin/sh", "-c", exec_cmd, NULL);
159 while (waitpid(pid, &status, 0) != pid)
161 if (WEXITSTATUS(status)) {
164 warnx("child exited with code %d", status);
167 syslog(LOG_ERR, "child exited with code %d", status);
177 write_emerg(const char *eme_msg, int *warn_cont)
179 if (*warn_cont == 0) {
180 openlog("battd", LOG_EMERG, LOG_CONSOLE);
181 syslog(LOG_ERR, "%s\n", eme_msg);
186 /* Check given numerical arguments. */
188 getnum(const char *str)
194 val = strtol(str, &ep, 10);
196 err(1, "strtol failed: %s", str);
198 if (str == ep || *ep != '\0')
199 errx(1, "invalid value: %s", str);
201 if (val > INT_MAX || val < INT_MIN) {
203 errc(1, errno, "getnum failed:");
210 main(int argc, char **argv)
212 struct battd_conf battd_options, *opts;
214 int fp_device, exec_cont;
215 int per_warn_cont, time_warn_cont, stat_warn_cont;
216 int check_sec, time_def_alert, def_warn_cont;
220 opts = &battd_options;
223 * As default, we sleep for 30 seconds before
224 * we next call get_apm_info and do the rounds.
225 * The lower the value, the more accurate. Very
226 * low values could cause a decrease in system
227 * performance. We recommend about 30 seconds.
232 exec_cont = per_warn_cont = stat_warn_cont = 0;
233 time_warn_cont = time_def_alert = def_warn_cont = 0;
236 opts->alert_time = 0;
237 opts->alert_status = -1;
238 opts->exec_cmd = NULL;
239 opts->apm_dev = APM_DEVICE;
241 while ((c = getopt(argc, argv, "de:Ep:s:c:f:ht:T")) != -1) {
244 /* Parse the check battery interval. */
245 check_sec = getnum(optarg);
247 errx(1, "the interval for checking battery"
248 "status must be greater than 0.");
257 /* Command to execute. */
258 opts->exec_cmd = optarg;
261 /* Only execute once when any condition has been met. */
265 /* Don't use /dev/apm use optarg. */
266 opts->apm_dev = optarg;
269 /* Print usage and be done! */
274 * Parse percentage to alert on and enable
275 * battd to monitor the battery percentage.
277 opts->alert_per = getnum(optarg);
278 if (opts->alert_per <= 0 || opts->alert_per > 100)
279 errx(1, "Battery percentage to alert on must be "
280 "greater than 0 and less than 100.");
284 * Parse status to alert on and enable
285 * battd to monitor the battery status.
286 * We also accept 'high', 'HIGH' or 'HiGh'.
288 if (strcasecmp(optarg, "high") == 0)
289 opts->alert_status = 0; /* High */
290 else if (strcasecmp(optarg, "low") == 0)
291 opts->alert_status = 1; /* Low */
292 else if (strcasecmp(optarg, "critical") == 0)
293 opts->alert_status = 2; /* Critical (mental) */
295 /* No idea, see what we have. */
296 opts->alert_status = getnum(optarg);
297 if (opts->alert_status < 0 || opts->alert_status > 2)
298 errx(1, "Alert status must be between 0 and 2.");
303 * Parse time to alert on and enable
304 * battd to monitor the time percentage.
306 opts->alert_time = getnum(optarg);
307 if (opts->alert_time <= 0)
308 errx(1, "Alert time must be greater than 0 minutes.");
318 if ((fp_device = open(opts->apm_dev, O_RDONLY)) < 0)
319 err(1, "open failed: %s", opts->apm_dev);
322 * Before we become a daemon, first check whether
323 * the actual function requested is supported. If
324 * not, exit and let the user know.
328 get_apm_info(&ai, fp_device, opts->apm_dev, ERR_OUT);
330 if (opts->alert_per >= 0)
331 if (check_percent(ai.ai_batt_life))
332 errx(1, "invalid/unknown percentage(%d) returned from %s",
333 ai.ai_batt_life, opts->apm_dev);
335 if (opts->alert_time || time_def_alert)
336 if (check_time(ai.ai_batt_time))
337 errx(1, "invalid/unknown time(%d) returned from %s",
338 ai.ai_batt_time, opts->apm_dev);
340 if (opts->alert_status)
341 if (check_stat(ai.ai_batt_stat))
342 errx(1, "invalid/unknown status(%d) returned from %s",
343 ai.ai_batt_stat, opts->apm_dev);
349 if (daemon(0, 0) == -1)
350 err(1, "daemon failed");
356 if (get_apm_info(&ai, fp_device, opts->apm_dev,
358 f_debug ? ERR_OUT : SYSLOG_OUT))
362 /* Recoverable - sleep for check_sec seconds */
365 /* If we have power, reset the warning values. */
366 if (ai.ai_acline == AC_LINE_IN) {
374 * If the battery has main power (AC lead is plugged in)
375 * we skip and sleep for check_sec seconds.
377 if (ai.ai_acline != AC_LINE_IN) {
379 * Battery has no mains power. Time to do
384 * Main Processing loop
385 * --------------------
386 * 1. Check battery percentage if enabled.
387 * 2. Check battery time remaining if enabled.
388 * 3. Check battery status if enabled.
389 * 4. Deal with default alerts.
392 /* 1. Check battery percentage if enabled */
393 if (opts->alert_per) {
394 if (check_percent(ai.ai_batt_life)) {
397 printf("Invalid percentage (%d) received from %s.\n",
398 ai.ai_batt_life, opts->apm_dev);
401 syslog(LOG_ERR, "Invalid percentage received from %s.",
409 if (ai.ai_batt_life <= (u_int)opts->alert_per) {
410 tmp = (ai.ai_batt_life == (u_int)opts->alert_per);
411 snprintf(msg, sizeof(msg), "battery has %s %d%%\n",
412 tmp ? "reached" : "fallen below",
414 execute_cmd(opts->exec_cmd, &exec_cont);
415 write_emerg(msg, &per_warn_cont);
419 /* 2. Check battery time remaining if enabled */
420 if (opts->alert_time) {
421 if (check_time(ai.ai_batt_time)) {
424 printf("Invalid time value (%d) received from %s.\n",
425 ai.ai_batt_time, opts->apm_dev);
428 syslog(LOG_ERR, "Invalid time value received from %s.",
436 if (ai.ai_batt_time <= (opts->alert_time * SECONDS)) {
438 char tmp_time[sizeof "tt:tt:tt" + 1];
444 snprintf(tmp_time, sizeof(tmp_time), "%d:%d:%d\n", h, m, s);
445 tmp = (ai.ai_batt_time == opts->alert_time);
446 snprintf(msg, sizeof(msg), "battery has %s %d(%s) minutes"
447 "remaining\n", tmp ? "reached" : "fallen below",
448 ai.ai_batt_time / SECONDS, tmp_time);
449 execute_cmd(opts->exec_cmd, &exec_cont);
450 write_emerg(msg, &time_warn_cont);
454 /* 3. Check battery status if enabled */
455 if (opts->alert_status != -1) {
456 if (check_stat(ai.ai_batt_stat)) {
459 printf("Invalid status value (%d) received from %s.\n",
460 ai.ai_batt_life, opts->apm_dev);
463 syslog(LOG_ERR, "Invalid status value received from %s.",
471 if (ai.ai_batt_stat >= (u_int)opts->alert_status) {
472 const char *batt_status[] = {"high", "low", "critical"};
474 tmp = (ai.ai_batt_stat == (u_int)opts->alert_status);
475 snprintf(msg, sizeof(msg), "battery has %s '%s' status\n",
476 tmp ? "reached" : "fallen below",
477 batt_status[ai.ai_batt_stat]);
478 execute_cmd(opts->exec_cmd, &exec_cont);
479 write_emerg(msg, &stat_warn_cont);
483 /* 4. Deal with default alerts. */
484 if (time_def_alert) {
485 if (check_time(ai.ai_batt_time)) {
486 if (ai.ai_batt_time <= DEFAULT_ALERT * SECONDS) {
487 snprintf(msg, sizeof(msg), "WARNING! battery only"
488 "has roughly %d minutes remaining!\n",
489 ai.ai_batt_time / SECONDS);
490 write_emerg(msg, NULL);
495 if (ai.ai_batt_life <= DEFAULT_ALERT) {
496 tmp = (ai.ai_batt_life == DEFAULT_ALERT);
497 snprintf(msg, sizeof(msg), "WARNING! battery has %s %d%%\n",
498 tmp ? "reached" : "fallen below",
501 execute_cmd(opts->exec_cmd, &exec_cont);
502 write_emerg(msg, &def_warn_cont);
507 /* Sleep time! Default is 30 seconds */