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 /*
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #include <sys/lvm/mdmn_commd.h>
28 #include <stdio.h>
29 #include <stdlib.h> /* getenv, exit */
30 #include <signal.h>
31 #include <unistd.h>
32 #include <sys/types.h>
33 #include <memory.h>
34 #include <stropts.h>
35 #include <netconfig.h>
36 #include <sys/resource.h> /* rlimit */
37 #include <syslog.h>
38 #include <meta.h>
39
40 #ifdef DEBUG
41 #define RPC_SVC_FG
42 #endif
43
44 /*
45 * This means we shutdown rpc.mdcommd at some time in the window
46 * after 1201 seconds and before 2400 seconds of inactivity.
47 */
48 #define _RPCSVC_CLOSEDOWN 2400
49
50 #ifdef RPC_SVC_FG
51 static int _rpcpmstart; /* Started by a port monitor ? */
52 #endif /* RPC_SVC_FG */
53 /* States a server can be in wrt request */
54
55 #define _IDLE 0
56 #define _SERVED 1
57
58 static int _rpcsvcstate = _IDLE; /* Set when a request is serviced */
59 static int _rpcsvccount = 0; /* Number of requests being serviced */
60
61 extern int mdmn_send_svc_2();
62 extern int *mdmn_work_svc_2();
63 extern int *mdmn_wakeup_initiator_svc_2();
64 extern int *mdmn_wakeup_master_svc_2();
65 extern int *mdmn_comm_lock_svc_2();
66 extern int *mdmn_comm_unlock_svc_2();
67 extern int *mdmn_comm_suspend_svc_2();
68 extern int *mdmn_comm_resume_svc_2();
69 extern int *mdmn_comm_reinit_set_svc_2();
70 extern int *mdmn_comm_msglock_svc_2();
71
72
73 static void
_msgout(msg)74 _msgout(msg)
75 char *msg;
76 {
77 #ifdef RPC_SVC_FG
78 if (_rpcpmstart)
79 syslog(LOG_ERR, "%s", msg);
80 else
81 (void) fprintf(stderr, "%s\n", msg);
82 #else
83 syslog(LOG_ERR, "%s", msg);
84 #endif
85 }
86
87 static void
closedown(void)88 closedown(void)
89 {
90 if (_rpcsvcstate == _IDLE && _rpcsvccount == 0) {
91 int size;
92 int i, openfd = 0;
93
94 size = svc_max_pollfd;
95 for (i = 0; i < size && openfd < 2; i++)
96 if (svc_pollfd[i].fd >= 0)
97 openfd++;
98 if (openfd <= 1)
99 exit(0);
100 } else
101 _rpcsvcstate = _IDLE;
102
103 (void) signal(SIGALRM, (void(*)()) closedown);
104 (void) alarm(_RPCSVC_CLOSEDOWN/2);
105 }
106
107 static void
mdmn_commd_2(rqstp,transp)108 mdmn_commd_2(rqstp, transp)
109 struct svc_req *rqstp;
110 register SVCXPRT *transp;
111 {
112 union {
113 md_mn_msg_t mdmn_send_1_arg;
114 md_mn_msg_t mdmn_work_1_arg;
115 md_mn_result_t mdmn_wakeup_1_arg;
116 md_mn_msgclass_t mdmn_comm_lock_1_arg;
117 md_mn_msgclass_t mdmn_comm_unlock_1_arg;
118 uint_t mdmn_comm_reinit_1_arg;
119 } argument;
120 char *result;
121 bool_t (*_xdr_argument)(), (*_xdr_result)();
122 char *(*local)();
123 int free_result = 0;
124
125 _rpcsvccount++;
126 switch (rqstp->rq_proc) {
127 case NULLPROC:
128 (void) svc_sendreply(transp, xdr_void,
129 (char *)NULL);
130 _rpcsvccount--;
131 _rpcsvcstate = _SERVED;
132 svc_done(transp);
133 return;
134
135 case mdmn_send:
136 _xdr_argument = xdr_md_mn_msg_t;
137 _xdr_result = xdr_md_mn_result_t;
138 (void) memset((char *)&argument, 0, sizeof (argument));
139 if (!svc_getargs(transp, _xdr_argument, (caddr_t)&argument)) {
140 svcerr_decode(transp);
141 svc_done(transp);
142 _rpcsvccount--;
143 _rpcsvcstate = _SERVED;
144 return;
145 }
146 /*
147 * mdmn_send_2 will not always do a sendreply.
148 * it will register in a table and let the mdmn_wakeup1
149 * do the sendreply for that call.
150 * in order to register properly we need the transp handle
151 * If we get a 0 back from mdmn_send_svc_2() we have no pending
152 * RPC in-flight, so we drop the service count.
153 */
154 if (mdmn_send_svc_2((md_mn_msg_t *)&argument, rqstp) == 0) {
155 _rpcsvccount--;
156 _rpcsvcstate = _SERVED;
157 svc_done(rqstp->rq_xprt);
158 }
159
160 return; /* xdr_free is called by mdmn_wakeup_initiator_svc_2 */
161
162 case mdmn_work:
163 _xdr_argument = xdr_md_mn_msg_t;
164 _xdr_result = xdr_int;
165 local = (char *(*)()) mdmn_work_svc_2;
166 free_result = 1;
167 break;
168
169 case mdmn_wakeup_master:
170 _xdr_argument = xdr_md_mn_result_t;
171 _xdr_result = xdr_int;
172 local = (char *(*)()) mdmn_wakeup_master_svc_2;
173 free_result = 1;
174 break;
175
176 case mdmn_wakeup_initiator:
177 /*
178 * We must have had an in-flight RPC request to get here,
179 * so drop the in-flight count.
180 */
181 _xdr_argument = xdr_md_mn_result_t;
182 _xdr_result = xdr_int;
183 local = (char *(*)()) mdmn_wakeup_initiator_svc_2;
184 free_result = 1;
185 _rpcsvccount--;
186 break;
187
188 case mdmn_comm_lock:
189 _xdr_argument = xdr_md_mn_set_and_class_t;
190 _xdr_result = xdr_int;
191 local = (char *(*)()) mdmn_comm_lock_svc_2;
192 break;
193
194 case mdmn_comm_unlock:
195 _xdr_argument = xdr_md_mn_set_and_class_t;
196 _xdr_result = xdr_int;
197 local = (char *(*)()) mdmn_comm_unlock_svc_2;
198 break;
199
200 case mdmn_comm_suspend:
201 _xdr_argument = xdr_md_mn_set_and_class_t;
202 _xdr_result = xdr_int;
203 local = (char *(*)()) mdmn_comm_suspend_svc_2;
204 break;
205
206 case mdmn_comm_resume:
207 _xdr_argument = xdr_md_mn_set_and_class_t;
208 _xdr_result = xdr_int;
209 local = (char *(*)()) mdmn_comm_resume_svc_2;
210 break;
211
212 case mdmn_comm_reinit_set:
213 _xdr_argument = xdr_u_int;
214 _xdr_result = xdr_int;
215 local = (char *(*)()) mdmn_comm_reinit_set_svc_2;
216 break;
217
218 case mdmn_comm_msglock:
219 _xdr_argument = xdr_md_mn_type_and_lock_t;
220 _xdr_result = xdr_int;
221 local = (char *(*)()) mdmn_comm_msglock_svc_2;
222 break;
223
224 default:
225 svcerr_noproc(transp);
226 _rpcsvccount--;
227 _rpcsvcstate = _SERVED;
228 svc_done(transp);
229 return;
230 }
231 (void) memset((char *)&argument, 0, sizeof (argument));
232 if (!svc_getargs(transp, _xdr_argument, (caddr_t)&argument)) {
233 svcerr_decode(transp);
234 _rpcsvccount--;
235 _rpcsvcstate = _SERVED;
236 svc_done(transp);
237 return;
238 }
239 result = (*local)(&argument, rqstp);
240 if (_xdr_result && result != NULL &&
241 !svc_sendreply(transp, _xdr_result, result)) {
242 svcerr_systemerr(transp);
243 }
244 if (!svc_freeargs(transp, _xdr_argument, (caddr_t)&argument)) {
245 _msgout(gettext("unable to free arguments"));
246 svc_done(transp);
247 exit(1);
248 }
249
250 if (free_result == 1) {
251 free(result);
252 }
253
254 svc_done(transp);
255 _rpcsvccount--;
256 _rpcsvcstate = _SERVED;
257 }
258
259 /*
260 * atexit handler to flag the lack of commd to the kernel so that we don't
261 * panic due to RPC failures when the commd has been killed.
262 */
263 static void
exit_commd()264 exit_commd()
265 {
266 md_error_t ep = mdnullerror;
267 syslog(LOG_DAEMON | LOG_DEBUG, gettext("mdcommd exiting"));
268 (void) metaioctl(MD_MN_SET_COMMD_RUNNING, 0, &ep, "rpc.mdcommd");
269 }
270
271 /* ARGSUSED */
272 int
main()273 main()
274 {
275 pid_t pid;
276 int i;
277 md_error_t ep = mdnullerror;
278 int mode = RPC_SVC_MT_USER;
279
280 (void) sigset(SIGPIPE, SIG_IGN);
281
282 /*
283 * Attempt to set MT_USER behaviour for mdcommd service routines.
284 * If this isn't done, there is a possibility that the transport
285 * handle might be freed before the thread created by mdmn_send_svc_2
286 * can use it. A consequence of this is that svc_done() must be
287 * called on the handle when it's no longer needed.
288 */
289 if (rpc_control(RPC_SVC_MTMODE_SET, &mode) == FALSE) {
290 _msgout(gettext("cannot set MT_USER mode for RPC service"));
291 exit(1);
292 }
293
294 /*
295 * If stdin looks like a TLI endpoint, we assume
296 * that we were started by a port monitor. If
297 * t_getstate fails with TBADF, this is not a
298 * TLI endpoint.
299 */
300 if (t_getstate(0) != -1 || t_errno != TBADF) {
301 char *netid;
302 struct netconfig *nconf = NULL;
303 SVCXPRT *transp;
304 int pmclose;
305
306 #ifdef RPC_SVC_FG
307 _rpcpmstart = 1;
308 #endif /* RPC_SVC_FG */
309 openlog("mdmn_commd", LOG_PID, LOG_DAEMON);
310
311 if ((netid = getenv("NLSPROVIDER")) == NULL) {
312 /* started from inetd */
313 pmclose = 1;
314 } else {
315 if ((nconf = getnetconfigent(netid)) == NULL)
316 _msgout(gettext("cannot get transport info"));
317
318 pmclose = (t_getstate(0) != T_DATAXFER);
319 }
320 if ((transp = svc_tli_create(0, nconf, NULL, 0, 0)) == NULL) {
321 _msgout(gettext("cannot create server handle"));
322 exit(1);
323 }
324 if (nconf)
325 freenetconfigent(nconf);
326 if (!svc_reg(transp, MDMN_COMMD, TWO, mdmn_commd_2, 0)) {
327 _msgout(gettext(
328 "unable to register (MDMN_COMMD, TWO)."));
329 exit(1);
330 }
331
332 (void) atexit(exit_commd);
333
334 if (pmclose) {
335 (void) signal(SIGALRM, (void(*)()) closedown);
336 (void) alarm(_RPCSVC_CLOSEDOWN/2);
337 }
338
339 pid = getpid();
340 (void) metaioctl(MD_MN_SET_COMMD_RUNNING, (void *)pid, &ep,
341 "rpc.mdcommd");
342 svc_run();
343 exit(1);
344 /* NOTREACHED */
345 } else {
346 #ifndef RPC_SVC_FG
347 #pragma weak closefrom
348 /* LINTED */
349 extern void closefrom();
350 int size;
351 struct rlimit rl;
352 pid = fork();
353 if (pid < 0) {
354 perror(gettext("cannot fork"));
355 exit(1);
356 }
357 if (pid)
358 exit(0);
359 if (closefrom != NULL)
360 closefrom(0);
361 else {
362 rl.rlim_max = 0;
363 (void) getrlimit(RLIMIT_NOFILE, &rl);
364 if ((size = rl.rlim_max) == 0)
365 exit(1);
366 for (i = 0; i < size; i++)
367 (void) close(i);
368 }
369 i = open("/dev/null", 2);
370 (void) dup2(i, 1);
371 (void) dup2(i, 2);
372 (void) setsid();
373 openlog("mdmn_commd", LOG_PID, LOG_DAEMON);
374 #endif
375 }
376 if (!svc_create(mdmn_commd_2, MDMN_COMMD, TWO, "tcp")) {
377 _msgout(gettext("unable to create (MDMN_COMMD, TWO) for tcp."));
378 exit(1);
379 }
380
381 (void) atexit(exit_commd);
382 (void) metaioctl(MD_MN_SET_COMMD_RUNNING, (void *)1, &ep,
383 "rpc.mdcommd");
384
385 svc_run();
386 _msgout(gettext("svc_run returned"));
387 return (1);
388 }
389