1/*	$NetBSD: apm.c,v 1.20 2008/05/02 19:59:19 xtraeme Exp $ */
2
3/*-
4 * Copyright (c) 1996 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by John Kohl.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <sys/types.h>
33#include <sys/ioctl.h>
34#include <sys/socket.h>
35#include <sys/time.h>
36#include <sys/un.h>
37
38#include <machine/apmvar.h>
39
40#include <err.h>
41#include <errno.h>
42#include <fcntl.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46#include <unistd.h>
47
48#include "pathnames.h"
49#include "apm-proto.h"
50
51#define	FALSE 0
52#define	TRUE 1
53
54__dead static void	usage(void);
55__dead static void	zzusage(void);
56static int	do_zzz(const char *, enum apm_action);
57static int	open_socket(const char *);
58static int	send_command(int, struct apm_command *, struct apm_reply *);
59
60static void
61usage(void)
62{
63
64	fprintf(stderr,"usage: %s [-v] [-z | -S] [-abdlms] [-f socket]\n",
65	    getprogname());
66	exit(1);
67}
68
69static void
70zzusage(void)
71{
72
73	fprintf(stderr,"usage: %s [-z | -S] [-f socket]\n",
74	    getprogname());
75	exit(1);
76}
77
78static int
79send_command(int fd,
80    struct apm_command *cmd,
81    struct apm_reply *reply)
82{
83
84	/* send a command to the apm daemon */
85	cmd->vno = APMD_VNO;
86
87	if (send(fd, cmd, sizeof(*cmd), 0) == sizeof(*cmd)) {
88		if (recv(fd, reply, sizeof(*reply), 0) != sizeof(*reply)) {
89			warn("invalid reply from APM daemon");
90			return (1);
91		}
92	} else {
93		warn("invalid send to APM daemon");
94		return (1);
95	}
96	return (0);
97}
98
99static int
100do_zzz(const char *pn, enum apm_action action)
101{
102	struct apm_command command;
103	struct apm_reply reply;
104	int fd;
105
106	switch (action) {
107	case NONE:
108	case SUSPEND:
109		command.action = SUSPEND;
110		break;
111	case STANDBY:
112		command.action = STANDBY;
113		break;
114	default:
115		zzusage();
116	}
117
118	fd = open_socket(pn);
119	if (fd == -1)
120		err(1, "cannot open connection to APM daemon");
121	printf("Suspending system...\n");
122	exit(send_command(fd, &command, &reply));
123}
124
125static int
126open_socket(const char *sockname)
127{
128	struct sockaddr_un s_un;
129	int sock, errr;
130
131	sock = socket(AF_LOCAL, SOCK_STREAM, 0);
132	if (sock == -1)
133		err(1, "cannot create local socket");
134
135	s_un.sun_family = AF_LOCAL;
136	strncpy(s_un.sun_path, sockname, sizeof(s_un.sun_path));
137	s_un.sun_len = SUN_LEN(&s_un);
138	if (connect(sock, (struct sockaddr *)&s_un, s_un.sun_len) == -1) {
139		errr = errno;
140		close(sock);
141		errno = errr;
142		return (-1);
143	}
144	return (sock);
145}
146
147int
148main(int argc, char *argv[])
149{
150	struct apm_command command;
151	struct apm_reply reply;
152	struct apm_power_info *api = &reply.batterystate;
153	const char *sockname = _PATH_APM_SOCKET;
154	enum apm_action action = NONE;
155	int ch, doac, dobstate, domin, dopct, dostatus, fd, nodaemon,
156	    rval, verbose;
157
158	doac = dobstate = domin = dopct = dostatus = nodaemon =
159	    verbose = FALSE;
160	while ((ch = getopt(argc, argv, "Sabdf:lmsvz")) != -1)
161		switch (ch) {
162		case 'v':
163			verbose = TRUE;
164			break;
165		case 'f':
166			sockname = optarg;
167			break;
168		case 'z':
169			if (action != NONE)
170				usage();
171			action = SUSPEND;
172			break;
173		case 'S':
174			if (action != NONE)
175				usage();
176			action = STANDBY;
177			break;
178		case 's':
179			if (action != NONE && action != GETSTATUS)
180				usage();
181			dostatus = TRUE;
182			action = GETSTATUS;
183			break;
184		case 'b':
185			if (action != NONE && action != GETSTATUS)
186				usage();
187			dobstate = TRUE;
188			action = GETSTATUS;
189			break;
190		case 'l':
191			if (action != NONE && action != GETSTATUS)
192				usage();
193			dopct = TRUE;
194			action = GETSTATUS;
195			break;
196		case 'm':
197			if (action != NONE && action != GETSTATUS)
198				usage();
199			domin = TRUE;
200			action = GETSTATUS;
201			break;
202		case 'a':
203			if (action != NONE && action != GETSTATUS)
204				usage();
205			doac = TRUE;
206			action = GETSTATUS;
207			break;
208		case 'd':
209			nodaemon = TRUE;
210			break;
211		case '?':
212		default:
213			usage();
214		}
215
216	if (strcmp(getprogname(), "zzz") == 0)
217		exit(do_zzz(sockname, action));
218
219	if (nodaemon)
220		fd = -1;
221	else
222		fd = open_socket(sockname);
223
224	switch (action) {
225	case NONE:
226		verbose = doac = dopct = domin = dobstate = dostatus = TRUE;
227		action = GETSTATUS;
228		/* FALLTHROUGH */
229	case GETSTATUS:
230		if (fd == -1) {
231			/* open the device directly and get status */
232			fd = open(_PATH_APM_NORMAL, O_RDONLY);
233			if (fd == -1) {
234				err(1, "cannot contact APM daemon and "
235				    "cannot open "
236				    _PATH_APM_NORMAL);
237			}
238			memset(&reply, 0, sizeof(reply));
239			if (ioctl(fd, APM_IOC_GETPOWER,
240			    &reply.batterystate) == -1)
241				err(1, "ioctl(APM_IOC_GETPOWER)");
242			goto printval;
243		}
244		/* FALLTHROUGH */
245	case SUSPEND:
246	case STANDBY:
247		if (nodaemon && fd == -1) {
248			fd = open(_PATH_APM_CTLDEV, O_RDWR);
249			if (fd == -1)
250				err(1, "cannot open APM control device "
251				    _PATH_APM_CTLDEV);
252			sync();
253			sync();
254			sleep(1);
255			if (ioctl(fd, action == SUSPEND ?
256			    APM_IOC_SUSPEND : APM_IOC_STANDBY, 0) == -1)
257				err(1, "cannot enter requested power state");
258			printf("System will enter %s in a moment.\n",
259			    action == SUSPEND ? "suspend mode" :
260			    "standby mode");
261			exit(0);
262		} else if (fd == -1)
263			err(1, "cannot contact APM daemon at socket "
264			    _PATH_APM_SOCKET);
265		command.action = action;
266		break;
267	default:
268		usage();
269	}
270
271	if ((rval = send_command(fd, &command, &reply)) == 0) {
272		switch (action) {
273		case GETSTATUS:
274printval:
275			if (verbose) {
276				if (dobstate)
277					printf("Battery charge state: %s\n",
278					    battstate(api->battery_state));
279
280				if (dopct && domin && api->minutes_left == 0)
281					domin = FALSE;
282
283				if (dopct || domin) {
284					printf("Battery remaining: ");
285					if (dopct)
286						printf("%d percent",
287						    api->battery_life);
288					if (dopct && domin)
289						printf(" (");
290					if (domin)
291						printf("%d minutes",
292						    api->minutes_left);
293					if (dopct && domin)
294						printf(")");
295					printf("\n");
296				}
297				if (doac)
298					printf("A/C adapter state: %s\n",
299					    ac_state(api->ac_state));
300				if (dostatus)
301					printf("Power management enabled\n");
302				if (api->nbattery) {
303					printf("Number of batteries: %u\n",
304					    api->nbattery);
305				}
306			} else {
307				if (dobstate)
308					printf("%d\n", api->battery_state);
309				if (dopct)
310					printf("%d\n", api->battery_life);
311				if (domin)
312					printf("%d\n", api->minutes_left);
313				if (doac)
314					printf("%d\n", api->ac_state);
315				if (dostatus)
316					printf("1\n");
317			}
318			break;
319		default:
320			break;
321		}
322		switch (reply.newstate) {
323		case SUSPEND:
324			printf("System will enter suspend mode "
325			    "in a moment.\n");
326			break;
327		case STANDBY:
328			printf("System will enter standby mode "
329			    "in a moment.\n");
330			break;
331		default:
332			break;
333		}
334	} else
335		errx(rval, "cannot get reply from APM daemon");
336
337	exit(0);
338}
339