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.12 2008/02/22 04:30:34 swildner Exp $
36 * Dedicated to my grandfather Peter Foy. Goodnight...
40 #include <sys/ioctl.h>
41 #include <sys/types.h>
44 #include <machine/apm_bios.h>
56 #define APMUNKNOWN 255 /* Unknown value. */
57 #define AC_LINE_IN 1 /* AC Line Status values. */
60 #define APM_DEVICE "/dev/apm" /* Default APM Device. */
61 #define ERR_OUT 1 /* Values for error writing. */
63 #define DEFAULT_ALERT 10 /* Default alert is 10%. */
69 int alert_per
; /* Percentage to alert user on */
70 int alert_time
; /* User can also alert when there is only X amount of minutes left */
71 int alert_status
; /* Alert when battery is either high, low, critical */
72 const char *apm_dev
; /* APM Device */
73 const char *exec_cmd
; /* Command to execute if desired */
80 static int check_percent(int);
81 static int check_stat(int);
82 static int check_time(int);
83 static int get_apm_info(struct apm_info
*, int, const char *, int);
84 static void execute_cmd(const char *, int *);
85 static void write_emerg(const char *, int *);
86 static void usage(void) __dead2
;
92 fprintf(stderr
, "usage: battd [-dEhT] [-c seconds] [-e command] [-f device]\n"
93 " [-p percent] [-s status] [-t minutes]\n");
95 fprintf(stderr
, "usage: battd [-EhT] [-c seconds] [-e command] [-f device]\n"
96 " [-p percent] [-s status] [-t minutes]\n");
102 check_percent(int apm_per
)
104 if (apm_per
< 0 || apm_per
>= APMUNKNOWN
|| apm_per
> 100)
111 check_time(int apm_time
)
120 check_stat(int apm_stat
)
122 if (apm_stat
> 3 || apm_stat
< 0)
128 /* Fetch battery information */
130 get_apm_info(struct apm_info
*ai
, int fp_dev
, const char *apm_dev
, int err_to
)
132 if (ioctl(fp_dev
, APMIO_GETINFO
, ai
) == -1) {
134 err(1, "ioctl(APMIO_GETINFO) device: %s", apm_dev
);
136 syslog(LOG_ERR
, "ioctl(APMIO_GETINFO) device: %s: %m ",
145 /* Execute command. */
147 execute_cmd(const char *exec_cmd
, int *exec_cont
)
152 if (*exec_cont
!= EXEC_DONE
) {
153 if ((pid
= fork()) == -1) {
154 /* Here fork failed */
160 syslog(LOG_ERR
, "fork failed: %m");
161 } else if (pid
== 0) {
162 execl("/bin/sh", "/bin/sh", "-c", exec_cmd
, NULL
);
165 while (waitpid(pid
, &status
, 0) != pid
)
167 if (WEXITSTATUS(status
)) {
170 warnx("child exited with code %d", status
);
173 syslog(LOG_ERR
, "child exited with code %d", status
);
175 if (*exec_cont
== EXEC_ONCE
)
176 *exec_cont
= EXEC_DONE
;
183 write_emerg(const char *eme_msg
, int *warn_cont
)
185 if (*warn_cont
== 0) {
186 openlog("battd", LOG_EMERG
, LOG_CONSOLE
);
187 syslog(LOG_ERR
, "%s\n", eme_msg
);
192 /* Check given numerical arguments. */
194 getnum(const char *str
)
200 val
= strtol(str
, &ep
, 10);
202 err(1, "strtol failed: %s", str
);
204 if (str
== ep
|| *ep
!= '\0')
205 errx(1, "invalid value: %s", str
);
207 if (val
> INT_MAX
|| val
< INT_MIN
) {
209 errc(1, errno
, "getnum failed:");
216 main(int argc
, char **argv
)
218 struct battd_conf battd_options
, *opts
;
220 int fp_device
, exec_cont
;
221 int per_warn_cont
, time_warn_cont
, stat_warn_cont
;
222 int check_sec
, time_def_alert
, def_warn_cont
;
226 opts
= &battd_options
;
229 * As default, we sleep for 30 seconds before
230 * we next call get_apm_info and do the rounds.
231 * The lower the value, the more accurate. Very
232 * low values could cause a decrease in system
233 * performance. We recommend about 30 seconds.
238 exec_cont
= EXEC_ALL
;
239 per_warn_cont
= stat_warn_cont
= 0;
240 time_warn_cont
= time_def_alert
= def_warn_cont
= 0;
242 opts
->alert_per
= DEFAULT_ALERT
;
243 opts
->alert_time
= 0;
244 opts
->alert_status
= -1;
245 opts
->exec_cmd
= NULL
;
246 opts
->apm_dev
= APM_DEVICE
;
248 while ((c
= getopt(argc
, argv
, "de:Ep:s:c:f:ht:T")) != -1) {
251 /* Parse the check battery interval. */
252 check_sec
= getnum(optarg
);
254 errx(1, "the interval for checking battery "
255 "status must be greater than 0.");
264 /* Command to execute. */
265 opts
->exec_cmd
= optarg
;
268 /* Only execute once when any condition has been met. */
269 exec_cont
= EXEC_ONCE
;
272 /* Don't use /dev/apm use optarg. */
273 opts
->apm_dev
= optarg
;
276 /* Print usage and be done! */
281 * Parse percentage to alert on and enable
282 * battd to monitor the battery percentage.
283 * A value of 0 disables battery percentage monitoring.
285 opts
->alert_per
= getnum(optarg
);
286 if (opts
->alert_per
< 0 || opts
->alert_per
> 99)
287 errx(1, "Battery percentage to alert on must be "
288 "between 0 and 99.");
292 * Parse status to alert on and enable
293 * battd to monitor the battery status.
294 * We also accept 'high', 'HIGH' or 'HiGh'.
296 if (strcasecmp(optarg
, "high") == 0)
297 opts
->alert_status
= 0; /* High */
298 else if (strcasecmp(optarg
, "low") == 0)
299 opts
->alert_status
= 1; /* Low */
300 else if (strcasecmp(optarg
, "critical") == 0)
301 opts
->alert_status
= 2; /* Critical (mental) */
303 /* No idea, see what we have. */
304 opts
->alert_status
= getnum(optarg
);
305 if (opts
->alert_status
< 0 || opts
->alert_status
> 2)
306 errx(1, "Alert status must be between 0 and 2.");
311 * Parse time to alert on and enable
312 * battd to monitor the time percentage.
314 opts
->alert_time
= getnum(optarg
);
315 if (opts
->alert_time
<= 0)
316 errx(1, "Alert time must be greater than 0 minutes.");
326 if ((fp_device
= open(opts
->apm_dev
, O_RDONLY
)) < 0)
327 err(1, "open failed: %s", opts
->apm_dev
);
330 * Before we become a daemon, first check whether
331 * the actual function requested is supported. If
332 * not, exit and let the user know.
336 get_apm_info(&ai
, fp_device
, opts
->apm_dev
, ERR_OUT
);
338 if (opts
->alert_per
> 0)
339 if (check_percent(ai
.ai_batt_life
))
340 errx(1, "invalid/unknown percentage(%d) returned from %s",
341 ai
.ai_batt_life
, opts
->apm_dev
);
343 if (opts
->alert_time
|| time_def_alert
)
344 if (check_time(ai
.ai_batt_time
) && ai
.ai_batt_time
!= -1)
345 errx(1, "invalid/unknown time(%d) returned from %s",
346 ai
.ai_batt_time
, opts
->apm_dev
);
348 if (opts
->alert_status
)
349 if (check_stat(ai
.ai_batt_stat
))
350 errx(1, "invalid/unknown status(%d) returned from %s",
351 ai
.ai_batt_stat
, opts
->apm_dev
);
357 if (daemon(0, 0) == -1)
358 err(1, "daemon failed");
365 if (get_apm_info(&ai
, fp_device
, opts
->apm_dev
,
367 f_debug
? ERR_OUT
: SYSLOG_OUT
))
371 /* Recoverable - sleep for check_sec seconds */
374 /* If we have power, reset the warning values. */
375 if (ai
.ai_acline
== AC_LINE_IN
) {
383 * If the battery has main power (AC lead is plugged in)
384 * we skip and sleep for check_sec seconds.
386 if (ai
.ai_acline
!= AC_LINE_IN
) {
388 * Battery has no mains power. Time to do
393 * Main Processing loop
394 * --------------------
395 * 1. Check battery percentage if enabled.
396 * 2. Check battery time remaining if enabled.
397 * 3. Check battery status if enabled.
398 * 4. Deal with time default alert.
401 /* 1. Check battery percentage if enabled */
402 if (opts
->alert_per
) {
403 if (check_percent(ai
.ai_batt_life
)) {
406 printf("Invalid percentage (%d) received from %s.\n",
407 ai
.ai_batt_life
, opts
->apm_dev
);
410 syslog(LOG_ERR
, "Invalid percentage received from %s.",
418 if (ai
.ai_batt_life
<= (u_int
)opts
->alert_per
) {
419 tmp
= (ai
.ai_batt_life
== (u_int
)opts
->alert_per
);
420 snprintf(msg
, sizeof(msg
), "battery has %s %d%%\n",
421 tmp
? "reached" : "fallen below",
423 execute_cmd(opts
->exec_cmd
, &exec_cont
);
424 write_emerg(msg
, &per_warn_cont
);
428 /* 2. Check battery time remaining if enabled */
429 if (opts
->alert_time
) {
430 if (check_time(ai
.ai_batt_time
)) {
433 printf("Invalid time value (%d) received from %s.\n",
434 ai
.ai_batt_time
, opts
->apm_dev
);
437 syslog(LOG_ERR
, "Invalid time value received from %s.",
445 if (ai
.ai_batt_time
<= (opts
->alert_time
* SECONDS
)) {
447 char tmp_time
[sizeof "tt:tt:tt" + 1];
453 snprintf(tmp_time
, sizeof(tmp_time
), "%d:%d:%d\n", h
, m
, s
);
454 tmp
= (ai
.ai_batt_time
== opts
->alert_time
);
455 snprintf(msg
, sizeof(msg
), "battery has %s %d(%s) minutes "
456 "remaining\n", tmp
? "reached" : "fallen below",
457 ai
.ai_batt_time
/ SECONDS
, tmp_time
);
458 execute_cmd(opts
->exec_cmd
, &exec_cont
);
459 write_emerg(msg
, &time_warn_cont
);
463 /* 3. Check battery status if enabled */
464 if (opts
->alert_status
!= -1) {
465 if (check_stat(ai
.ai_batt_stat
)) {
468 printf("Invalid status value (%d) received from %s.\n",
469 ai
.ai_batt_life
, opts
->apm_dev
);
472 syslog(LOG_ERR
, "Invalid status value received from %s.",
480 if (ai
.ai_batt_stat
>= (u_int
)opts
->alert_status
) {
481 const char *batt_status
[] = {"high", "low", "critical"};
483 tmp
= (ai
.ai_batt_stat
== (u_int
)opts
->alert_status
);
484 snprintf(msg
, sizeof(msg
), "battery has %s '%s' status\n",
485 tmp
? "reached" : "fallen below",
486 batt_status
[ai
.ai_batt_stat
]);
487 execute_cmd(opts
->exec_cmd
, &exec_cont
);
488 write_emerg(msg
, &stat_warn_cont
);
492 /* 4. Deal with time default alert. */
493 if (time_def_alert
) {
494 if (check_time(ai
.ai_batt_time
)) {
497 printf("Invalid time value (%d) received from %s.\n",
498 ai
.ai_batt_time
, opts
->apm_dev
);
501 syslog(LOG_ERR
, "Invalid time value received from %s.",
509 if (ai
.ai_batt_time
<= DEFAULT_ALERT
* SECONDS
) {
510 snprintf(msg
, sizeof(msg
), "WARNING! battery has "
511 "only roughly %d minutes remaining!\n",
512 ai
.ai_batt_time
/ SECONDS
);
513 write_emerg(msg
, &def_warn_cont
);
519 /* Sleep time! Default is 30 seconds */