mddoors.c revision 8452:89d32dfdae6e
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
33static pid_t enter_daemon_lock(void);
34static void exit_daemon_lock(void);
35#define	DAEMON_LOCK_FILE "/var/run/.mddoors.lock"
36
37static int hold_daemon_lock;
38static const char *daemon_lock_file = DAEMON_LOCK_FILE;
39static int daemon_lock_fd;
40
41void
42daemon_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 */
54static pid_t
55enter_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 */
88static void
89exit_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 */
120static void
121door2rpc(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 */
168int
169main(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