4 * Network server for apcupsd.
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,
29 static pthread_mutex_t mutex
= PTHREAD_MUTEX_INITIALIZER
;
32 static char largebuf
[4096];
41 /* forward referenced subroutines */
42 void *handle_client_request(void *arg
);
44 static void status_open(UPSINFO
*ups
)
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
60 static int status_close(UPSINFO
*ups
, int nsockfd
)
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) {
75 sptr
= eptr
= largebuf
;
79 if (net_send(nsockfd
, sptr
, eptr
- sptr
) <= 0)
87 if (net_send(nsockfd
, NULL
, 0) < 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
, ...)
109 avsnprintf(buf
, sizeof(buf
), fmt
, ap
);
112 if ((i
= (strlen(largebuf
) + strlen(buf
))) < (int)(sizeof(largebuf
) - 1)) {
113 astrncat(largebuf
, buf
, sizeof(largebuf
));
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 */
130 struct in_addr local_ip
;
132 for (tlog
= 0; (ups
= attach_ups(ups
)) == NULL
; tlog
-= 5 * 60) {
135 log_event(ups
, LOG_ERR
, "apcserver: Cannot attach SYSV IPC.\n");
140 local_ip
.s_addr
= INADDR_ANY
;
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) {
153 log_event(ups
, LOG_ERR
, "apcserver: cannot open stream socket");
158 /* Reuse old sockets */
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",
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;
176 log_event(ups
, LOG_ERR
, "apcserver: cannot bind port %d. ERR=%s",
177 ups
->statusport
, strerror(errno
));
181 listen(sockfd
, 5); /* tell system we are ready */
183 log_event(ups
, LOG_INFO
, "NIS server startup succeeded");
186 /* Wait for a connection from a client process. */
187 for (tlog
= 0; (newsockfd
= net_accept(sockfd
, &cli_addr
)) < 0; tlog
-= 5 * 60) {
190 log_event(ups
, LOG_ERR
, "apcserver: accept error. ERR=%s",
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
);
207 arg
= (struct s_arg
*)malloc(sizeof(struct s_arg
));
208 arg
->newsockfd
= newsockfd
;
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
225 void *handle_client_request(void *arg
)
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);
240 Error_abort0("Cannot attach SYSV IPC.\n");
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
,
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)
260 int stat
= output_events(nsockfd
, events_file
);
264 net_send(nsockfd
, notavail
, sizeof(notavail
));
265 net_send(nsockfd
, NULL
, 0);
269 } else if (strncmp("rawupsinfo", line
, 10) == 0) {
270 net_send(nsockfd
, (char *)ups
, sizeof(UPSINFO
));
271 if (net_send(nsockfd
, NULL
, 0) < 0)
273 } else if (strncmp("eprominfo", line
, 9) == 0) {
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)
286 net_send(nsockfd
, errmsg
, sizeof(errmsg
));
287 if (net_send(nsockfd
, NULL
, 0) < 0)
299 #else /* HAVE_NISSERVER */
301 void do_server(UPSINFO
*ups
)
303 log_event(ups
, LOG_ERR
, "apcserver: code not enabled in config.\n");
307 #endif /* HAVE_NISSERVER */
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
;
324 av0
= strrchr(av
, '/');
326 av0
++; /* strip everything before and including / */
330 request_init(&req
, RQ_DAEMON
, av0
, RQ_FILE
, newsock
, NULL
);
333 if (!hosts_access(&req
)) {
334 log_event(core_ups
, LOG_WARNING
,
335 "Connection from %.500s refused by tcp_wrappers.", eval_client(&req
));
339 #ifdef I_WANT_LOTS_OF_LOGGING
340 log_event(core_ups
, LOG_NOTICE
, "connect from %.500s", eval_client(&req
));
346 #endif /* HAVE_LIBWRAP */