1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <signal.h>
27 #include <sys/types.h>
28 #include <sys/time.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <netinet/tcp.h>
32 #include <netdb.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <stdlib.h>
37 #include <errno.h>
38
39 #include "cfg_lockd.h"
40
41 static daemonaddr_t clientaddr;
42 static daemonaddr_t server;
43
44 static unsigned short server_port = CFG_SERVER_PORT;
45 static int lock_soc = 0;
46 static int pf_inet = AF_INET;
47 static int locked;
48 static int initdone;
49 static int initresult;
50 static pid_t socket_pid;
51
52 static void cfg_lockd_reinit();
53
54 static int last_cmd = -1;
55 static uint8_t seq = 0;
56
57 static void
send_cmd(int cmd)58 send_cmd(int cmd)
59 {
60 struct lock_msg message_buf;
61 int rc;
62
63 if (last_cmd == cmd) {
64 message_buf.seq = seq;
65 } else {
66 message_buf.seq = ++seq;
67 last_cmd = cmd;
68 }
69 message_buf.message = cmd;
70 if ((message_buf.pid = getpid()) != socket_pid)
71 cfg_lockd_reinit();
72
73 do {
74 rc = sendto(lock_soc, &message_buf, sizeof (message_buf), 0,
75 (struct sockaddr *)&server, sizeof (server));
76 } while (rc == -1 && errno == EINTR);
77 #ifdef CFG_LOCKD_DEBUG
78 if (rc < 0) {
79 perror("send");
80 }
81 #endif
82 }
83
84 static void
read_msg(struct lock_msg * mp)85 read_msg(struct lock_msg *mp)
86 {
87 struct sockaddr from;
88 int rc, len;
89
90 /* wait for response */
91 do {
92 struct pollfd fds;
93
94 fds.fd = lock_soc;
95 fds.events = POLLIN;
96 fds.revents = 0;
97
98 rc = poll(&fds, 1, 500);
99 if (!rc) {
100 #ifdef CFG_LOCKD_DEBUG
101 fprintf(stderr, "LOCKD: resending last command (%d)\n",
102 last_cmd);
103 #endif
104 send_cmd(last_cmd);
105 }
106 } while (rc == 0 ||
107 (rc == -1 && errno == EINTR));
108
109 do {
110 len = sizeof (from);
111 rc = recvfrom(lock_soc, mp, sizeof (*mp), 0,
112 &from, &len);
113 } while (rc == -1 && errno == EINTR);
114 #ifdef CFG_LOCKD_DEBUG
115 if (rc < 0) {
116 perror("revcfrom");
117 }
118 #endif
119 }
120
121 static void
read_reply()122 read_reply()
123 {
124 struct lock_msg message_buf;
125
126 do {
127 read_msg(&message_buf);
128 } while (message_buf.seq != seq || message_buf.message != LOCK_LOCKED);
129 }
130
131 static void
read_ack()132 read_ack()
133 {
134 struct lock_msg message_buf;
135
136 do {
137 read_msg(&message_buf);
138 } while (message_buf.seq != seq || message_buf.message != LOCK_ACK);
139 }
140
141 void
cfg_lockd_rdlock()142 cfg_lockd_rdlock()
143 {
144 #ifdef CFG_LOCKD_DEBUG
145 FILE *fp;
146 #endif
147
148 send_cmd(LOCK_READ);
149 locked = 1;
150 read_reply();
151
152 #ifdef CFG_LOCKD_DEBUG
153 fp = fopen("/tmp/locktag", "a");
154 if (fp) {
155 time_t t = time(0);
156 fprintf(fp, "%19.19s read lock acquired\n", ctime(&t));
157 fclose(fp);
158 }
159 sleep(3);
160 #endif
161 }
162
163 void
cfg_lockd_wrlock()164 cfg_lockd_wrlock()
165 {
166 #ifdef CFG_LOCKD_DEBUG
167 FILE *fp;
168 #endif
169
170 send_cmd(LOCK_WRITE);
171 locked = 1;
172 read_reply();
173
174 #ifdef CFG_LOCKD_DEBUG
175 fp = fopen("/tmp/locktag", "a");
176 if (fp) {
177 time_t t = time(0);
178 fprintf(fp, "%19.19s write lock acquired\n", ctime(&t));
179 fclose(fp);
180 }
181 sleep(3);
182 #endif
183 }
184
185 void
cfg_lockd_unlock()186 cfg_lockd_unlock()
187 {
188 #ifdef CFG_LOCKD_DEBUG
189 FILE *fp;
190 #endif
191
192 send_cmd(LOCK_NOTLOCKED);
193 read_ack();
194 locked = 0;
195
196 #ifdef CFG_LOCKD_DEBUG
197 fp = fopen("/tmp/locktag", "a");
198 if (fp) {
199 time_t t = time(0);
200 fprintf(fp, "%19.19s ----- lock released\n", ctime(&t));
201 fclose(fp);
202 }
203 sleep(3);
204 #endif
205 }
206
207 void
cfg_lockd_stat()208 cfg_lockd_stat()
209 {
210 send_cmd(LOCK_STAT);
211 }
212
213 cfglockd_t
cfg_lockedby(pid_t * pidp)214 cfg_lockedby(pid_t *pidp)
215 {
216 struct lock_msg message_buf;
217 send_cmd(LOCK_LOCKEDBY);
218 read_msg(&message_buf);
219 *pidp = message_buf.pid;
220 return ((cfglockd_t)message_buf.message);
221 }
222
223 static void
cfg_atexit()224 cfg_atexit()
225 {
226 if (locked)
227 cfg_lockd_unlock();
228 }
229
230 static int
cfg_lockd_socket()231 cfg_lockd_socket()
232 {
233 if ((lock_soc = socket(pf_inet, SOCK_DGRAM, 0)) < 0) {
234 #ifdef CFG_LOCKD_DEBUG
235 fprintf(stderr, "libcfg: failed to create socket\n");
236 perror("socket");
237 #endif
238 return (-1);
239 }
240 clientaddr.sin_family = AF_INET;
241 clientaddr.sin_addr.s_addr = INADDR_ANY;
242 clientaddr.sin_port = htons(0);
243 if (bind(lock_soc, (struct sockaddr *)&clientaddr,
244 sizeof (clientaddr)) < 0) {
245 #ifdef CFG_LOCKD_DEBUG
246 perror("bind");
247 #endif
248 return (-1);
249 }
250 socket_pid = getpid();
251 return (0);
252 }
253
254 /*
255 * Re-initialise after a fork has been detected.
256 *
257 * Needs to create a new socket for new process to receive messages
258 * from the lock daemon and enter pid into lock file so that the daemon
259 * can detect new processes exit if it doesn't call unlock first.
260 */
261
262 static void
cfg_lockd_reinit()263 cfg_lockd_reinit()
264 {
265 if (lock_soc)
266 close(lock_soc);
267 lock_soc = 0;
268 if (cfg_lockd_socket()) {
269 initresult = 0;
270 return;
271 }
272 cfg_enterpid();
273 initresult = 1;
274 }
275
276 int
cfg_lockd_init()277 cfg_lockd_init()
278 {
279 struct hostent *hp;
280 FILE *fp;
281 int pid = 0x12345678;
282
283 if (initdone) {
284 /* only perform reinit if init worked first time */
285 if (getpid() != socket_pid && initresult != 0)
286 cfg_lockd_reinit();
287 return (initresult);
288 }
289
290 initdone = 1;
291 initresult = 0;
292
293 /* check if there's a lock daemon out there */
294 if ((fp = fopen(CFG_PIDFILE, "r")) == NULL)
295 return (0);
296 if (fscanf(fp, "%d\n", &pid) != 1) {
297 fclose(fp);
298 return (0);
299 }
300 fclose(fp);
301 if (kill((pid_t)pid, 0) != 0)
302 return (0);
303
304 /* there is a lock daemon */
305 cfg_lfinit();
306 cfg_enterpid();
307 if (cfg_lockd_socket())
308 return (0);
309
310 if ((hp = gethostbyname("localhost")) == NULL) {
311 #ifdef CFG_LOCKD_DEBUG
312 fprintf(stderr, "Can't find hostent for %s\n", "localhost");
313 #endif
314 return (0);
315 }
316 (void) memcpy(&(server.sin_addr.s_addr), *(hp->h_addr_list),
317 sizeof (server.sin_addr));
318 server.sin_port = htons(server_port);
319 server.sin_family = hp->h_addrtype;
320 endhostent();
321 atexit(cfg_atexit);
322 initresult = 1;
323 return (1);
324 }
325