xref: /titanic_41/usr/src/cmd/lvm/rpc.mdcommd/mddoors.c (revision c93c462eec9d46f84d567abf52eb29a27c2e134b)
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 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <door.h>
28 #include <locale.h>
29 #include <meta.h>
30 #include <strings.h>
31 #include <syslog.h>
32 
33 static pid_t enter_daemon_lock(void);
34 static void exit_daemon_lock(void);
35 #define	DAEMON_LOCK_FILE "/var/run/.mddoors.lock"
36 
37 static int hold_daemon_lock;
38 static const char *daemon_lock_file = DAEMON_LOCK_FILE;
39 static int daemon_lock_fd;
40 
41 void
42 daemon_cleanup()
43 {
44 	if (hold_daemon_lock) {
45 		meta_mirror_resync_block_all();
46 		exit_daemon_lock();
47 	}
48 }
49 
50 /*
51  * Use an advisory lock to ensure that only one daemon process is
52  * active at any point in time.
53  */
54 static pid_t
55 enter_daemon_lock(void)
56 {
57 	struct flock	lock;
58 
59 	daemon_lock_fd = open(daemon_lock_file, O_CREAT|O_RDWR, 0644);
60 
61 	if (daemon_lock_fd < 0) {
62 		exit(-1);
63 	}
64 
65 	lock.l_type = F_WRLCK;
66 	lock.l_whence = SEEK_SET;
67 	lock.l_start = 0;
68 	lock.l_len = 0;
69 
70 	if (fcntl(daemon_lock_fd, F_SETLK, &lock) == -1) {
71 
72 		if (errno == EAGAIN || errno == EDEADLK) {
73 
74 			if (fcntl(daemon_lock_fd, F_GETLK, &lock) == -1) {
75 				exit(1);
76 			}
77 			return (lock.l_pid);
78 		}
79 	}
80 	hold_daemon_lock = 1;
81 	return (getpid());
82 }
83 
84 
85 /*
86  * Drop the advisory daemon lock, close lock file
87  */
88 static void
89 exit_daemon_lock(void)
90 {
91 	struct flock lock;
92 
93 	lock.l_type = F_UNLCK;
94 	lock.l_whence = SEEK_SET;
95 	lock.l_start = 0;
96 	lock.l_len = 0;
97 
98 	if (fcntl(daemon_lock_fd, F_SETLK, &lock) == -1) {
99 		syslog(LOG_DAEMON | LOG_DEBUG, gettext("unlock(%s) - %s"),
100 		    daemon_lock_file, strerror(errno));
101 		return;
102 	}
103 
104 	if (close(daemon_lock_fd) == -1) {
105 		syslog(LOG_DAEMON | LOG_DEBUG,
106 		    gettext("close(%s) failed - %s\n"),
107 		    daemon_lock_file, strerror(errno));
108 		return;
109 	}
110 	unlink(daemon_lock_file);
111 }
112 
113 /*
114  * Purpose of this routine is to accept a message from the local kernel and
115  * send this message using rpc to the master node.
116  * when an ok comes from the master we call door_return()
117  */
118 
119 /* ARGSUSED */
120 static void
121 door2rpc(void *cookie,		/* required by the doors infrastructure */
122 	char *argp,
123 	size_t arg_size,	/* required by the doors infrastructure */
124 	door_desc_t *dp,	/* required by the doors infrastructure */
125 	uint_t n_desc)		/* required by the doors infrastructure */
126 {
127 	int		err;
128 	int		size;
129 	md_error_t	ep = mdnullerror;
130 	md_mn_result_t	*result = NULL;
131 	md_mn_kresult_t	kresult;
132 
133 	md_mn_kmsg_t *kmsg = (md_mn_kmsg_t *)(void *)argp;
134 	err = mdmn_send_message(kmsg->kmsg_setno, kmsg->kmsg_type,
135 	    kmsg->kmsg_flags, kmsg->kmsg_recipient, (char *)&(kmsg->kmsg_data),
136 	    kmsg->kmsg_size, &result, &ep);
137 
138 	if (result == NULL) {
139 		kresult.kmmr_comm_state = MDMNE_RPC_FAIL;
140 	} else {
141 		kresult.kmmr_comm_state = result->mmr_comm_state;
142 		if (err == 0) {
143 			kresult.kmmr_msgtype = result->mmr_msgtype;
144 			kresult.kmmr_flags = result->mmr_flags;
145 			kresult.kmmr_exitval = result->mmr_exitval;
146 			kresult.kmmr_failing_node = result->mmr_failing_node;
147 			size = result->mmr_out_size;
148 			if (size > 0) {
149 				/* This is the max data we can transfer, here */
150 				if (size > MDMN_MAX_KRES_DATA) {
151 					size = MDMN_MAX_KRES_DATA;
152 				}
153 				bcopy(result->mmr_out, &(kresult.kmmr_res_data),
154 				    size);
155 				kresult.kmmr_res_size = size;
156 			} else {
157 				kresult.kmmr_res_size = 0;
158 			}
159 		}
160 		free_result(result);
161 	}
162 
163 	door_return((char *)&kresult, sizeof (md_mn_kresult_t), NULL, 0);
164 }
165 
166 
167 /* ARGSUSED */
168 int
169 main(void)
170 {
171 
172 	int		i;
173 	int		mdmn_door_handle;
174 	pid_t		pid;
175 	int		size;
176 	md_error_t	ep = mdnullerror;
177 	struct rlimit	rl;
178 
179 	/*
180 	 * Get the locale set up before calling any other routines
181 	 * with messages to ouput.  Just in case we're not in a build
182 	 * environment, make sure that TEXT_DOMAIN gets set to
183 	 * something.
184 	 */
185 #if !defined(TEXT_DOMAIN)
186 #define	TEXT_DOMAIN "SYS_TEST"
187 #endif
188 	(void) setlocale(LC_ALL, "");
189 	(void) textdomain(TEXT_DOMAIN);
190 
191 	openlog("mddoors", LOG_PID, LOG_DAEMON);
192 
193 	/* here beginneth the daemonizing code */
194 	pid = fork();
195 	if (pid < 0) {
196 		syslog(LOG_DAEMON | LOG_ERR, gettext("Cannot fork"));
197 		exit(1);
198 	}
199 
200 	if (pid) {
201 		exit(0);
202 	}
203 
204 	/*
205 	 * Only one daemon can run at a time.
206 	 * If another instance is already running, this is not an error.
207 	 */
208 	if ((pid = enter_daemon_lock()) != getpid()) {
209 		exit(0);
210 	}
211 
212 	rl.rlim_max = 0;
213 	getrlimit(RLIMIT_NOFILE, &rl);
214 	if ((size = rl.rlim_max) == 0) {
215 		syslog(LOG_DAEMON | LOG_ERR, gettext("Cannot getrlimit"));
216 		exit(1);
217 	}
218 
219 	for (i = 0; i < size; i++) {
220 		if (i == daemon_lock_fd)
221 			continue;
222 		(void) close(i);
223 	}
224 
225 
226 	i = open("/dev/null", 2);
227 	(void) dup2(i, 1);
228 	(void) dup2(i, 2);
229 	setsid();
230 
231 	/* here endeth the daemonizing code */
232 
233 	/* Block out the usual signals so we don't get killed unintentionally */
234 	(void) signal(SIGHUP, SIG_IGN);
235 	(void) signal(SIGINT, SIG_IGN);
236 	(void) signal(SIGQUIT, SIG_IGN);
237 	(void) signal(SIGTERM, SIG_IGN);
238 
239 	atexit(daemon_cleanup);
240 
241 	/* Resume any previously blocked resync */
242 	meta_mirror_resync_unblock_all();
243 
244 	/*
245 	 * At this point we are single threaded.
246 	 * We give mdmn_send_message() a chance to initialize safely.
247 	 */
248 	(void) mdmn_send_message(0, 0, 0, 0, 0, 0, 0, 0);
249 
250 	/* setup the door handle */
251 	mdmn_door_handle = door_create(door2rpc, NULL,
252 	    DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
253 	if (mdmn_door_handle == -1) {
254 		perror(gettext("door_create failed"));
255 		syslog(LOG_DAEMON | LOG_ERR, gettext("door_create failed"));
256 		exit(1);
257 	}
258 
259 	if (metaioctl(MD_MN_SET_DOORH, &mdmn_door_handle, &ep,
260 	    "mddoors") != 0) {
261 		syslog(LOG_DAEMON | LOG_DEBUG, gettext(
262 		    "Couldn't set door handle"));
263 		exit(1);
264 	}
265 
266 	(void) pause();
267 	syslog(LOG_DAEMON | LOG_ERR, gettext(
268 	    "Unexpected exit from pause()"));
269 	return (1);
270 }
271