UPS: apcupsd clean sources
[tomato.git] / release / src / router / apcupsd / src / apcnis.c
blob62663c62637e6e52f49c76384d06867c46a41cd0
1 /*
2 * apcnis.c
4 * Network server for apcupsd.
5 */
7 /*
8 * Copyright (C) 1999-2006 Kern Sibbald
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.
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.
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., 59 Temple Place - Suite 330, Boston,
22 * MA 02111-1307, USA.
25 #include "apc.h"
27 #ifdef HAVE_NISSERVER
29 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
32 static char largebuf[4096];
33 static int stat_recs;
35 struct s_arg {
36 UPSINFO *ups;
37 int newsockfd;
41 /* forward referenced subroutines */
42 void *handle_client_request(void *arg);
44 static void status_open(UPSINFO *ups)
46 P(mutex);
47 largebuf[0] = 0;
48 stat_recs = 0;
51 #define STAT_REV 1
54 * Send the status lines across the network one line
55 * at a time (to prevent sending too large a buffer).
57 * Returns -1 on error or EOF
58 * 0 OK
60 static int status_close(UPSINFO *ups, int nsockfd)
62 int i;
63 char buf[MAXSTRING];
64 char *sptr, *eptr;
66 i = strlen(largebuf);
67 asnprintf(buf, sizeof(buf), "APC : %03d,%03d,%04d\n",
68 STAT_REV, stat_recs, i);
70 if (net_send(nsockfd, buf, strlen(buf)) <= 0) {
71 V(mutex);
72 return -1;
75 sptr = eptr = largebuf;
76 for (; i > 0; i--) {
77 if (*eptr == '\n') {
78 eptr++;
79 if (net_send(nsockfd, sptr, eptr - sptr) <= 0)
80 break;
81 sptr = eptr;
82 } else {
83 eptr++;
87 if (net_send(nsockfd, NULL, 0) < 0) {
88 V(mutex);
89 return -1;
92 V(mutex);
93 return 0;
99 * Buffer up the status messages so that they can be sent
100 * by the status_close() routine over the network.
102 static void status_write(UPSINFO *ups, const char *fmt, ...)
104 va_list ap;
105 int i;
106 char buf[MAXSTRING];
108 va_start(ap, fmt);
109 avsnprintf(buf, sizeof(buf), fmt, ap);
110 va_end(ap);
112 if ((i = (strlen(largebuf) + strlen(buf))) < (int)(sizeof(largebuf) - 1)) {
113 astrncat(largebuf, buf, sizeof(largebuf));
114 stat_recs++;
115 } else {
116 log_event(ups, LOG_ERR,
117 "apcserver.c: Status buffer overflow %d bytes\n", i - sizeof(largebuf));
122 void do_server(UPSINFO *ups)
124 int newsockfd, sockfd, childpid;
125 struct sockaddr_in cli_addr; /* client's address */
126 struct sockaddr_in serv_addr; /* our address */
127 int tlog;
128 int turnon = 1;
129 struct s_arg *arg;
130 struct in_addr local_ip;
132 for (tlog = 0; (ups = attach_ups(ups)) == NULL; tlog -= 5 * 60) {
133 if (tlog <= 0) {
134 tlog = 60 * 60;
135 log_event(ups, LOG_ERR, "apcserver: Cannot attach SYSV IPC.\n");
137 sleep(5 * 60);
140 local_ip.s_addr = INADDR_ANY;
142 if (ups->nisip[0]) {
143 if (inet_pton(AF_INET, ups->nisip, &local_ip) != 1) {
144 log_event(ups, LOG_WARNING, "Invalid NISIP specified: '%s'", ups->nisip);
145 local_ip.s_addr = INADDR_ANY;
149 /* Open a TCP socket */
150 for (tlog = 0; (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0; tlog -= 5 * 60) {
151 if (tlog <= 0) {
152 tlog = 60 * 60;
153 log_event(ups, LOG_ERR, "apcserver: cannot open stream socket");
155 sleep(5 * 60);
158 /* Reuse old sockets */
159 #ifndef HAVE_MINGW
160 if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void*)&turnon, sizeof(turnon)) < 0) {
161 log_event(ups, LOG_WARNING, "Cannot set SO_REUSEADDR on socket: %s\n",
162 strerror(errno));
164 #endif
166 /* Bind our local address so that the client can send to us. */
167 memset((char *)&serv_addr, 0, sizeof(serv_addr));
168 serv_addr.sin_family = AF_INET;
169 serv_addr.sin_addr = local_ip;
170 serv_addr.sin_port = htons(ups->statusport);
172 for (tlog = 0; bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0;
173 tlog -= 5 * 60) {
174 if (tlog <= 0) {
175 tlog = 60 * 60;
176 log_event(ups, LOG_ERR, "apcserver: cannot bind port %d. ERR=%s",
177 ups->statusport, strerror(errno));
179 sleep(5 * 60);
181 listen(sockfd, 5); /* tell system we are ready */
183 log_event(ups, LOG_INFO, "NIS server startup succeeded");
185 for (;;) {
186 /* Wait for a connection from a client process. */
187 for (tlog = 0; (newsockfd = net_accept(sockfd, &cli_addr)) < 0; tlog -= 5 * 60) {
188 if (tlog <= 0) {
189 tlog = 60 * 60;
190 log_event(ups, LOG_ERR, "apcserver: accept error. ERR=%s",
191 strerror(errno));
193 sleep(5 * 60);
196 #ifdef HAVE_LIBWRAP
198 * This function checks the incoming client and if it's not
199 * allowed closes the connection.
201 if (check_wrappers(argvalue, newsockfd) == FAILURE) {
202 net_close(newsockfd);
203 continue;
205 #endif
207 arg = (struct s_arg *)malloc(sizeof(struct s_arg));
208 arg->newsockfd = newsockfd;
209 arg->ups = ups;
210 childpid = 0;
212 pthread_t tid;
213 pthread_create(&tid, NULL, handle_client_request, arg);
219 * Accept requests from client. Send output one line
220 * at a time followed by a zero length transmission.
222 * Return when the connection is terminated or there
223 * is an error.
225 void *handle_client_request(void *arg)
227 FILE *events_file;
228 char line[MAXSTRING];
229 char errmsg[] = "Invalid command\n";
230 char notavail[] = "Not available\n";
231 char notrun[] = "Apcupsd not running\n";
232 int nsockfd = ((struct s_arg *)arg)->newsockfd;
233 UPSINFO *ups = ((struct s_arg *)arg)->ups;
235 pthread_detach(pthread_self());
236 if ((ups = attach_ups(ups)) == NULL) {
237 net_send(nsockfd, notrun, sizeof(notrun));
238 net_send(nsockfd, NULL, 0);
239 free(arg);
240 Error_abort0("Cannot attach SYSV IPC.\n");
243 for (;;) {
244 /* Read command */
245 if (net_recv(nsockfd, line, MAXSTRING) <= 0)
246 break; /* connection terminated */
248 if (strncmp("status", line, 6) == 0) {
249 if (output_status(ups, nsockfd, status_open, status_write,
250 status_close) < 0) {
251 break;
253 } else if (strncmp("events", line, 6) == 0) {
254 if ((ups->eventfile[0] == 0) ||
255 ((events_file = fopen(ups->eventfile, "r")) == NULL)) {
256 net_send(nsockfd, notavail, sizeof(notavail));
257 if (net_send(nsockfd, NULL, 0) < 0)
258 break;
259 } else {
260 int stat = output_events(nsockfd, events_file);
262 fclose(events_file);
263 if (stat < 0) {
264 net_send(nsockfd, notavail, sizeof(notavail));
265 net_send(nsockfd, NULL, 0);
266 break;
269 } else if (strncmp("rawupsinfo", line, 10) == 0) {
270 net_send(nsockfd, (char *)ups, sizeof(UPSINFO));
271 if (net_send(nsockfd, NULL, 0) < 0)
272 break;
273 } else if (strncmp("eprominfo", line, 9) == 0) {
274 int len;
276 len = strlen(ups->eprom) + 1;
277 net_send(nsockfd, ups->eprom, len);
278 len = strlen(ups->firmrev) + 1;
279 net_send(nsockfd, ups->firmrev, len);
280 len = strlen(ups->upsmodel) + 1;
281 net_send(nsockfd, ups->upsmodel, len);
283 if (net_send(nsockfd, NULL, 0) < 0)
284 break;
285 } else {
286 net_send(nsockfd, errmsg, sizeof(errmsg));
287 if (net_send(nsockfd, NULL, 0) < 0)
288 break;
292 net_close(nsockfd);
294 free(arg);
295 detach_ups(ups);
296 return NULL;
299 #else /* HAVE_NISSERVER */
301 void do_server(UPSINFO *ups)
303 log_event(ups, LOG_ERR, "apcserver: code not enabled in config.\n");
304 exit(1);
307 #endif /* HAVE_NISSERVER */
310 #ifdef HAVE_LIBWRAP
313 * Unfortunately this function is also used by the old network code
314 * so for now compile it in anyway.
316 int allow_severity = LOG_INFO;
317 int deny_severity = LOG_WARNING;
319 int check_wrappers(char *av, int newsock)
321 struct request_info req;
322 char *av0;
324 av0 = strrchr(av, '/');
325 if (av0)
326 av0++; /* strip everything before and including / */
327 else
328 av0 = av;
330 request_init(&req, RQ_DAEMON, av0, RQ_FILE, newsock, NULL);
331 fromhost(&req);
333 if (!hosts_access(&req)) {
334 log_event(core_ups, LOG_WARNING,
335 "Connection from %.500s refused by tcp_wrappers.", eval_client(&req));
336 return FAILURE;
339 #ifdef I_WANT_LOTS_OF_LOGGING
340 log_event(core_ups, LOG_NOTICE, "connect from %.500s", eval_client(&req));
341 #endif
343 return SUCCESS;
346 #endif /* HAVE_LIBWRAP */