fix systemd unit install path
[external/acpid.git] / sock.c
1 /*
2  *  sock.c - ACPI daemon socket interface
3  *
4  *  Portions Copyright (C) 2000 Andrew Henroid
5  *  Portions Copyright (C) 2001 Sun Microsystems
6  *  Portions Copyright (C) 2004 Tim Hockin (thockin@hockin.org)
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include <unistd.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <errno.h>
30 #include <grp.h>
31
32 #include "acpid.h"
33 #include "log.h"
34 #include "event.h"
35 #include "ud_socket.h"
36 #include "connection_list.h"
37
38 const char *socketfile = ACPID_SOCKETFILE;
39 const char *socketgroup;
40 mode_t socketmode = ACPID_SOCKETMODE;
41 int clientmax = ACPID_CLIENTMAX;
42
43 /* the number of non-root clients that are connected */
44 int non_root_clients;
45
46 /* determine if a file descriptor is in fact a socket */
47 int
48 is_socket(int fd)
49 {
50         int v;
51         socklen_t l = sizeof(int);
52
53         return (getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&v, &l) == 0);
54 }
55
56 /* accept a new client connection */
57 static void
58 process_sock(int fd)
59 {
60         int cli_fd;
61         struct ucred creds;
62         char buf[32];
63         static int accept_errors;
64
65         /* accept and add to our lists */
66         cli_fd = ud_accept(fd, &creds);
67         if (cli_fd < 0) {
68                 acpid_log(LOG_ERR, "can't accept client: %s",
69                           strerror(errno));
70                 accept_errors++;
71                 if (accept_errors >= 5) {
72                         acpid_log(LOG_ERR, "giving up");
73                         clean_exit_with_status(EXIT_FAILURE);
74                 }
75                 return;
76         }
77         accept_errors = 0;
78
79         /* don't allow too many non-root clients  */
80         if (creds.uid != 0 && non_root_clients >= clientmax) {
81                 close(cli_fd);
82                 acpid_log(LOG_ERR, "too many non-root clients");
83                 return;
84         }
85         if (creds.uid != 0) {
86                 non_root_clients++;
87         }
88
89     /* don't leak fds when execing */
90         if (fcntl(cli_fd, F_SETFD, FD_CLOEXEC) < 0) {
91                 close(cli_fd);
92                 acpid_log(LOG_ERR, "fcntl() on client for FD_CLOEXEC: %s", 
93             strerror(errno));
94                 return;
95     }
96
97     /* don't allow clients to block this */
98     if (fcntl(cli_fd, F_SETFL, O_NONBLOCK) < 0) {
99                 close(cli_fd);
100                 acpid_log(LOG_ERR, "fcntl() on client for O_NONBLOCK: %s", 
101             strerror(errno));
102                 return;
103     }
104
105     snprintf(buf, sizeof(buf)-1, "%d[%d:%d]",
106                  creds.pid, creds.uid, creds.gid);
107         acpid_add_client(cli_fd, buf);
108 }
109
110 /* set up the socket for client connections */
111 void
112 open_sock()
113 {
114         int fd;
115         struct connection c;
116
117         /* if this is a socket passed in via stdin by systemd */
118         if (is_socket(STDIN_FILENO)) {
119                 fd = STDIN_FILENO;
120         } else {
121                 /* create our own socket */
122                 fd = ud_create_socket(socketfile);
123                 if (fd < 0) {
124                         acpid_log(LOG_ERR, "can't open socket %s: %s",
125                                 socketfile, strerror(errno));
126                         exit(EXIT_FAILURE);
127                 }
128
129                 if (chmod(socketfile, socketmode) < 0) {
130                         close(fd);
131                         acpid_log(LOG_ERR, "chmod() on socket %s: %s", 
132                         socketfile, strerror(errno));
133                         return;
134                 }
135
136                 /* if we need to change the socket's group, do so */
137                 if (socketgroup) {
138                         struct group *gr;
139                         struct stat buf;
140
141                     gr = getgrnam(socketgroup);
142                         if (!gr) {
143                                 acpid_log(LOG_ERR, "group %s does not exist", socketgroup);
144                                 exit(EXIT_FAILURE);
145                         }
146                         if (stat(socketfile, &buf) < 0) {
147                                 acpid_log(LOG_ERR, "can't stat %s: %s", 
148                             socketfile, strerror(errno));
149                                 exit(EXIT_FAILURE);
150                         }
151                         if (chown(socketfile, buf.st_uid, gr->gr_gid) < 0) {
152                                 acpid_log(LOG_ERR, "can't chown %s: %s", 
153                             socketfile, strerror(errno));
154                                 exit(EXIT_FAILURE);
155                         }
156                 }
157         }
158
159         /* don't leak fds when execing */
160         if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) {
161                 close(fd);
162                 acpid_log(LOG_ERR, "fcntl() on socket %s for FD_CLOEXEC: %s", 
163                           socketfile, strerror(errno));
164                 return;
165         }
166
167         /* avoid a potential hang */
168         if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
169                 close(fd);
170                 acpid_log(LOG_ERR, "fcntl() on socket %s for O_NONBLOCK: %s", 
171                           socketfile, strerror(errno));
172                 return;
173         }
174         
175         /* add a connection to the list */
176         c.fd = fd;
177         c.process = process_sock;
178         c.pathname = NULL;
179         c.kybd = 0;
180         add_connection(&c);
181 }