1 /*
2 * (c) Copyright 1990, Kim Fabricius Storm. All rights reserved.
3 * Copyright (c) 1996-2005 Michael T Pins. All rights reserved.
4 *
5 * Master/slave communication and locking.
6 */
7
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <signal.h>
11 #include <errno.h>
12 #include <pwd.h>
13 #include <string.h>
14 #include "config.h"
15 #include "global.h"
16 #include "hostname.h"
17 #include "proto.h"
18
19 /* proto.c */
20
21 static void write_lock(char *lock, char *operation);
22 static int read_lock(char *lock);
23
24
25 #ifndef ACCOUNTING
26
27 #ifdef AUTHORIZE
28 #define ACCOUNTING
29 #endif
30
31 #endif
32
33 extern char *master_directory, *db_directory;
34
35 #define HOSTBUF 80
36 char proto_host[HOSTBUF]; /* host having the lock */
37
38 /*
39 * When setting a lock, we must check a little later that
40 * we really got the lock set, i.e. that another process
41 * didn't set it at the same time!
42 */
43
44 #define LOCK_SAFETY 5 /* seconds */
45
46 /*
47 * proto_lock(program, mode)
48 *
49 * Returns:
50 * -1 Not running.
51 * 0 Running, no permission (PL_WAKEUP_SOFT)
52 * 0 Lock set (PL_SET)
53 * 1 Lock not set (PL_SET) (another is running)
54 * 1 Locked and running (PL_WAKEUP)
55 * pid Locked and running (PL_CHECK)
56 */
57
58 static void
write_lock(char * lock,char * operation)59 write_lock(char *lock, char *operation)
60 {
61 FILE *m_pid;
62 char host[HOSTBUF];
63
64 m_pid = open_file(lock, OPEN_CREATE);
65 if (m_pid == NULL)
66 sys_error("Cannot %s lock file: %s", operation, lock);
67 nn_gethostname(host, HOSTBUF);
68 fprintf(m_pid, "%d\n%s\n", process_id, host);
69 fclose(m_pid);
70 }
71
72 static int
read_lock(char * lock)73 read_lock(char *lock)
74 {
75 FILE *m_pid;
76 char host[HOSTBUF];
77 char pid[10];
78
79 pid[0] = NUL;
80 proto_host[0] = NUL;
81
82 m_pid = open_file(lock, OPEN_READ);
83 if (m_pid == NULL)
84 return -2; /* no lock */
85 fgets(pid, 10, m_pid);
86 fgets(proto_host, HOSTBUF, m_pid);
87 fclose(m_pid);
88
89 if (pid[0] == NUL)
90 return 0; /* corrupted lock */
91
92 if (proto_host[0] != NUL) {
93 substchr(proto_host, NL, NUL);
94 nn_gethostname(host, HOSTBUF);
95 if (strncmp(proto_host, host, HOSTBUF) != 0)
96 return -1; /* locked by another host */
97 proto_host[0] = NUL;
98 }
99 return atoi(pid);
100 }
101
102 int
proto_lock(int prog,int command)103 proto_lock(int prog, int command)
104 {
105 int try, pid;
106 char *lock = NULL;
107
108 switch (prog) {
109 case I_AM_MASTER:
110 case I_AM_EXPIRE:
111 lock = relative(master_directory, "MPID");
112 break;
113 case I_AM_SPEW:
114 lock = relative(master_directory, "WPID");
115 break;
116
117 #ifdef ACCOUNTING
118 case I_AM_ACCT:
119 lock = relative(db_directory, "LCK..acct");
120 break;
121 #endif
122
123 case I_AM_NN:
124 lock = relative(nn_directory, "LOCK");
125 break;
126 default:
127 sys_error("Invalid LOCK prog");
128 }
129
130 if (command == PL_TRANSFER) {
131 write_lock(lock, "transfer");
132 return 1;
133 }
134 if (command == PL_CLEAR)
135 goto rm_lock;
136
137 try = 1;
138 again:
139
140 switch (pid = read_lock(lock)) {
141 case -2:
142 goto no_lock;
143 case -1: /* wrong host */
144 return 1;
145 case 0:
146 case 1:
147 case 2: /* corrupted lock file */
148 if (who_am_i == I_AM_NN)
149 goto rm_lock;
150 if (--try < 0)
151 goto rm_lock;
152 sleep(LOCK_SAFETY); /* maybe it is being written */
153 goto again;
154 default:
155 break;
156 }
157
158 if (kill(pid, command == PL_TERMINATE ? SIGHUP : SIGALRM) == 0) {
159 switch (command) {
160 case PL_SET_QUICK:
161 sleep(1);
162 goto again;
163
164 case PL_SET_WAIT:
165 case PL_CLEAR_WAIT:
166 sleep(30);
167 goto again;
168
169 case PL_CHECK:
170 return pid;
171
172 default:
173 return 1;
174 }
175 }
176 if (command == PL_CHECK)
177 return (errno == EPERM) ? pid : -1;
178 if (command == PL_WAKEUP_SOFT)
179 return (errno == EPERM) ? 0 : -1;
180
181 /* lock file contains a non-existing process, or a process with */
182 /* wrong owner, ie. neither master or expire, so remove it */
183
184 rm_lock:
185 unlink(lock);
186
187 no_lock:
188 if (command != PL_SET && command != PL_SET_QUICK && command != PL_SET_WAIT)
189 return -1;
190
191 write_lock(lock, "create");
192
193 /* a user will not start nn twice at the exact same time! */
194 if (who_am_i == I_AM_NN || command == PL_SET_QUICK)
195 return 0;
196
197 sleep(LOCK_SAFETY);
198
199 if (read_lock(lock) != process_id)
200 return 1; /* somebody stole the lock file */
201 return 0; /* lock is set */
202 }
203
204 FILE *
open_gate_file(int mode)205 open_gate_file(int mode)
206 {
207 char *gate, *err;
208 FILE *gf;
209
210 gate = relative(master_directory, "GATE");
211 gf = open_file(gate, mode);
212 err = NULL;
213
214 switch (mode) {
215 case OPEN_READ:
216 if (gf != NULL) {
217 if (unlink(gate) == 0)
218 break;
219 err = "unlink";
220 break;
221 }
222 if (errno != ENOENT)
223 err = "read";
224 break;
225
226 default:
227 if (gf != NULL)
228 chmod(gate, 0644); /* override restrictive umask */
229 /* caller must complain if cannot open! */
230 break;
231 }
232
233 if (err != NULL) {
234 sys_warning("Cannot %s %s (err=%d). Check modes!!", err, gate, errno);
235 if (gf != NULL)
236 fclose(gf);
237 return NULL;
238 }
239 return gf;
240 }
241
242 void
send_master(char command,group_header * gh,int opt,long arg)243 send_master(char command, group_header * gh, int opt, long arg)
244 {
245 FILE *gate;
246
247 gate = open_gate_file(OPEN_APPEND);
248
249 if (gate == NULL) {
250 printf("Cannot send to master (check GATE file)\n");
251 return;
252 }
253 fprintf(gate, "%c;%ld;%c;%ld;%s %s;\n",
254 command, gh == NULL ? -1L : gh->group_num, opt, arg,
255 user_name(), date_time((time_t) 0));
256
257 fclose(gate);
258
259 log_entry('A', "SEND %c %s %c %ld",
260 command, gh == NULL ? "(all)" : gh->group_name, opt, arg);
261
262 if (who_am_i == I_AM_ADMIN)
263 proto_lock(I_AM_MASTER, PL_WAKEUP_SOFT);
264 }
265