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