?? apmd.c
字號:
time(&pre_suspend_time); pre_suspend_percentage = apmi->battery_percentage; /* Logging is okay here since the sync() happens afterward */ switch (event) { case APM_SYS_STANDBY: apmd_log(event, "System Standby"); break; case APM_USER_STANDBY: apmd_log(event, "User Standby"); break; } return apm_standby(apmd_fd);}/* Resume handler. Call the proxy, then update the battery status. * This also handles the update time function, which does much of the * same thing. */static void apmd_resume(apm_event_t event, apm_info * apmi){ char msg[512]; int len = 0; ADEBUG(4, "0x%04x\n", event); apmd_call_proxy(event, apmi); /* Logging */ switch (event) { case APM_NORMAL_RESUME: len = sprintf(msg, "Normal Resume"); break; case APM_STANDBY_RESUME: len = sprintf(msg, "Standby Resume"); break; case APM_CRITICAL_RESUME: len = sprintf(msg, "Critical Resume"); break; case APM_UPDATE_TIME: len = sprintf(msg, "Update Time"); break; } last_charging_status = apmi->battery_status; time(&post_suspend_time); /* Establish a minor and major checkpoint */ major_checkpoint = post_suspend_time; major_percentage = apmi->battery_percentage; minor_checkpoint = post_suspend_time; minor_percentage = major_percentage; /* Update the battery status */ if (event != APM_UPDATE_TIME && pre_suspend_time && post_suspend_time) { int dt = post_suspend_time - pre_suspend_time; int dp = apmi->battery_percentage - pre_suspend_percentage; if (dt > 0) len += sprintf(msg + len, " after %s", apm_time(dt)); /* describe in days if the suspend was for a long time */ if (dt > 60 * 60 * RESUME_HOURS && apmi->battery_percentage > 0 && pre_suspend_percentage > 0 && last_charging_status != 0x03 && dp < 0) { len += sprintf(msg + len, ", %.2f%%/day", ((double) dp / (double) dt) * 60. * 60. * 24.); } } len += sprintf(msg + len, " (%d%% %s) %s power", apmi->battery_percentage, apm_time_nosec(apmd_time(apmi)), ac_descr(apmi->ac_line_status)); apmd_log(event, msg);}/* "low" battery handler -- BIOS decides what "low" means, may send alert; * APMD has a more configurable notion, dealt with similarly. */static void apmd_low_battery(apm_event_t event, apm_info * apmi){ char msg[512]; ADEBUG(4, "0x%04x\n", event); sprintf(msg, "Battery Low Notification from %s (%d%% %s)", (event == APM_LOW_BATTERY ? "APM BIOS" : "apmd"), apmi->battery_percentage, apm_time_nosec(apmd_time(apmi))); if (!quiet) warn(msg);}/* Check battery status, and decide whether to log or not. */static void apmd_check_power(apm_event_t event, apm_info * apmi){ int dt, dp, logflag = 0, len = 0, warning = 0; int charging = IS_CHARGING(*apmi); char msg[512]; double rate; static int last_battery_status = 0; static int last_ac_status = -1; ADEBUG(4, "0x%04x\n", event); /* Call proxy and perform special logging as needed */ switch (event) { case APM_POWER_STATUS_CHANGE: /* Power status changes can happen a LOT ... e.g. for each change of * estimated battery life (minutes, seconds, percent) when charging * or discharging, battery full, empty, add/remove battery, etc. * Invoking the proxy on each change can use up battery power fast, * * So we just report if AC status changes -- the main use for this is * to make sure that power usage is reduced when running on batteries. * (When the kernel tracks multiple batteries that might be a good * time to update this to report just a bit more.) */ if (apmi->ac_line_status != last_ac_status) { apmd_call_proxy(event, apmi); sprintf(msg, "Now using %s Power", ac_descr(apmi->ac_line_status)); apmd_log(event, msg); apmd_power_reset(event, apmi); last_ac_status = apmi->ac_line_status; } break; case APM_LOW_BATTERY:synthetic_low_battery: apmd_call_proxy(event, apmi); last_battery_status = 1; apmd_low_battery(event, apmi); break;#ifdef APM_CAPABILITY_CHANGE case APM_CAPABILITY_CHANGE: apmd_call_proxy(event, apmi); apmd_log(event, "Capability Change"); break;#endif } /* Check for "low" battery status. Note: this is not an infinite loop * because last_battery_status is guaranteed to change. */ if (!charging && last_battery_status != 1 && apmi->battery_status == 1) { event = APM_LOW_BATTERY; goto synthetic_low_battery; } /* Reset the low battery state if we are now charging, etc. */ if (charging || apmi->battery_status != 1) { last_battery_status = 0; } /* If the BIOS doesn't keep track of the battery, no point going further */ if (apmi->battery_percentage < 0) return; /* If battery is not charging and battery percentage is changing * AND below apmd's threshold then give low battery warning. Both * BIOS and APMD originate warnings, and generally at different * percentages; the BIOS one is fixed and not always useful. */ if (!charging && apmi->battery_percentage <= warn_level && (abs(minor_percentage - apmi->battery_percentage) > 0 || apmi->battery_percentage == 0) && !quiet) warning = 1; dt = time(0) - major_checkpoint; dp = major_percentage - apmi->battery_percentage; /* If no warning, or no change since last logged message, then do nothing */ if (!warning && (dt == 0 || dp == 0)) return; /* Decide whether to log the battery status. * First check is when battery is fully drained or charged */ logflag = (apmi->battery_percentage != minor_percentage) && ((apmi->battery_percentage == 0) || (apmi->battery_percentage == 100)); /* Second check is when battery percentage has decreased by * percent_change. */ logflag = logflag || (abs(minor_percentage - apmi->battery_percentage) >= (percent_change > 1 ? percent_change : 1)); /* Logging is triggered when any check is satisfied. */ if (!logflag && !warning) return; /* We are logging (either normal, or a warning) so establish a minor * checkpoint -- reduces redundant logging, to no more than once per * percentage point change. */ minor_percentage = apmi->battery_percentage; minor_checkpoint = time(0); rate = (double) dp / (double) dt; /* If we're warning about low batteries, do so ... */ if (warning != 0) { sprintf(msg, "Battery warning (%d%% %s)", apmi->battery_percentage, apm_time_nosec(apmd_time(apmi))); warn(msg); /* ... if that's all we're doing, finish */ if (!logflag) return; } /* Begin composing the log message. First part is charging status, * followed by rate (in percent per minute) */ len = sprintf(msg, "%s: %f", charging ? "Charge" : "Battery", rate * 60.0); /* Time from full charge, or time from last power status change (ie, * a major checkpoint) */ if ((!charging && major_percentage == 100) || (charging && major_percentage == 0)) len += sprintf(msg + len, " %s", apm_time_nosec(dt)); else len += sprintf(msg + len, " (%s)", apm_time_nosec(dt)); /* Computation of predicted battery lifetime */ if (charging) { len += sprintf(msg + len, " %s", apm_time_nosec((int) ((100.0 - apmi->battery_percentage) / -rate))); } else { len += sprintf(msg + len, " %s", apm_time_nosec((int) (apmi->battery_percentage / rate))); } sprintf(msg + len, " (%d%% %s)", apmi->battery_percentage, apm_time_nosec(apmd_time(apmi))); apmd_log(APMD_BATTERY, msg); /* Establish a major checkpoint if we have become fully charged or * fully drained */ if ((apmi->battery_percentage == 0 && major_percentage != 0) || (apmi->battery_percentage == 100 && major_percentage != 100)) { minor_percentage = apmi->battery_percentage; minor_checkpoint = time(0); }}static void sig_handler(int sig){ syslog(LOG_INFO, "Exiting"); apmd_call_proxy(APMD_STOP, 0); unlink(PID_FILE); exit(0);}int main(int argc, char **argv){ int debug = 0; int c; int fd; int pid; FILE *str; apm_info apminfo, *apmi; apm_event_t events[MAX_EVENTS]; static struct option longopts[] = { {"verbose", 0, 0, 'v'}, {"quiet", 0, 0, 'q'}, {"version", 0, 0, 'V'}, {"debug", 0, 0, 'd'}, {"percentage", 1, 0, 'p'}, {"warn", 1, 0, 'w'}, {"wall", 0, 0, 'W'}, {"check", 1, 0, 'c'}, {"apmd_proxy", 1, 0, 'P'}, {"help", 0, 0, '?'}, {NULL, 0, 0, 0}, }; switch (apm_exists()) { case 1: fprintf(stderr, "No APM support in kernel\n"); exit(1); case 2: fprintf(stderr, "Old APM support in kernel\n"); exit(2); } while ((c = getopt_long(argc, argv, /* "-d" not documented */ "uVvqdp:w:Wc:P:?", longopts, NULL)) != -1) switch (c) { case 'V': fprintf(stderr, "apmd version %s\n", VERSION); exit(0); break; case 'v': ++verbose; break; case 'q': ++quiet; break; case 'd': ++debug; break; case 'p': percent_change = atoi(optarg); break; case 'w': warn_level = atoi(optarg); break; case 'W': ++wall; break; case 'u': fprintf(stderr, "WARNING:\n" " apmd's internal clock setting has been replaced by the " "apmd_proxy!\n" " To set the clock to UTC, you must edit the apmd_proxy " "program.\n\n"); break; case 'c': check_power_time = atoi(optarg); break; case 'P': apmd_proxy = optarg; break; case '?': default: usage(); break; } if (!access(PID_FILE, R_OK)) { if ((str = fopen(PID_FILE, "r"))) { fscanf(str, "%d", &pid); fclose(str); if (!kill(pid, 0) || errno == EPERM) { fprintf(stderr, "An apmd is already running as process %d:\n" "If it is no longer running, remove %s\n", pid, PID_FILE); exit(1); } } } if ((apmd_uid = getuid())) { fprintf(stderr, "apmd: must be run as root\n"); exit(1); } openlog("apmd", (debug ? LOG_PERROR : 0) | LOG_PID | LOG_CONS, LOG_DAEMON); if (signal(SIGINT, SIG_IGN) != SIG_IGN) signal(SIGINT, sig_handler); if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) signal(SIGQUIT, sig_handler); if (signal(SIGTERM, SIG_IGN) != SIG_IGN) signal(SIGTERM, sig_handler); if (!debug) { /* detach */ if ((pid = fork())) { /* parent */ if ((str = fopen(PID_FILE, "w"))) { fprintf(str, "%d\n", pid); fclose(str); } exit(0); } /* child */ if (pid < 0) { syslog(LOG_INFO, "fork() failed: %m"); unlink(PID_FILE); exit(1); } /* Child. Follow the daemon rules in * W. Richard Stevens. Advanced Programming * in the UNIX Environment (Addison-Wesley * Publishing Co., 1992). Page 417.). */ if (setsid() < 0) { syslog(LOG_INFO, "setsid() failed: %m"); unlink(PID_FILE); exit(1); } chdir("/"); close(0); close(1); close(2); umask(0); } if ((fd = apm_open()) < 0) { syslog(LOG_INFO, "apm_open() failed: %m"); unlink(PID_FILE); exit(1); } if (!apm_read(&apminfo)) { apmd_init(&apminfo); apmd_fd = fd; } for (;;) { int n = apm_get_events(fd, check_power_time, events, MAX_EVENTS); int i; /* If the call was timed-out, then we cause a "simulated" * UPDATE_TIME event. */ if (n == 0) { n = 1; events[0] = APMD_SYNTHETIC_CHECK; } for (i = 0; i < n; i++) { if (apm_read(&apminfo)) continue; apmi = &apminfo; ADEBUG(4, " [event loop] 0x%04x\n", events[i]); if (verbose) syslog(LOG_INFO, "Event 0x%04x: %s", events[i], apm_event_name(events[i])); switch (events[i]) { case APM_SYS_STANDBY: case APM_USER_STANDBY: apmd_standby(events[i], apmi); break; case APM_SYS_SUSPEND: case APM_USER_SUSPEND: apmd_suspend(events[i], apmi); break; case APM_CRITICAL_SUSPEND: time(&pre_suspend_time); pre_suspend_percentage = 0; /* As fast as possible */ ioctl(fd, APM_IOC_SUSPEND, NULL); break; case APM_NORMAL_RESUME: case APM_STANDBY_RESUME: case APM_UPDATE_TIME: case APM_CRITICAL_RESUME: apmd_resume(events[i], apmi); apmd_check_power(events[i], apmi); break; case APMD_SYNTHETIC_CHECK: apmd_log(events[i], "performing APM status check"); apmd_check_power(events[i], apmi); break; case APM_LOW_BATTERY: case APM_POWER_STATUS_CHANGE:#ifdef APM_CAPABILITY_CHANGE case APM_CAPABILITY_CHANGE:#endif apmd_check_power(events[i], apmi); break; default: /* These aren't errors; see the APM BIOS 1.2 spec. * 0x000d-0x00ff reserved system events * 0x0100-0x01ff reserved device events * 0x0200-0x02ff OEM-defined * 0x0300-0xffff reserved */ syslog(LOG_ERR, "Received unknown APM event 0x%04x.", events[i]); } } } return 0;}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -