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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 
23 /*
24  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 
30 #include "mt.h"
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <sys/param.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <time.h>
38 #include <wait.h>
39 #include <fcntl.h>
40 #include <thread.h>
41 #include <unistd.h>
42 #include <errno.h>
43 #include <ucontext.h>
44 #include <syslog.h>
45 #include <rpcsvc/daemon_utils.h>
46 #include <libscf.h>
47 
48 static int open_daemon_lock(const char *, int);
49 static int is_auto_enabled(char *);
50 
51 /*
52  * Check an array of services and enable any that don't have the
53  * "application/auto_enable" property set to "false", which is
54  * the interface to turn off this behaviour (see PSARC 2004/739).
55  */
56 void
57 _check_services(char **svcs)
58 {
59 	char *s;
60 
61 	for (; *svcs; svcs++) {
62 		if (is_auto_enabled(*svcs) == 0)
63 			continue;
64 		if ((s = smf_get_state(*svcs)) != NULL) {
65 			if (strcmp(SCF_STATE_STRING_DISABLED, s) == 0)
66 				(void) smf_enable_instance(*svcs,
67 				    SMF_TEMPORARY);
68 			free(s);
69 		}
70 	}
71 }
72 
73 /*
74  * Use an advisory lock to ensure that only one daemon process is
75  * active in the system at any point in time. If the lock is held
76  * by another process, do not block but return the pid owner of
77  * the lock to the caller immediately. The lock is cleared if the
78  * holding daemon process exits for any reason even if the lock
79  * file remains, so the daemon can be restarted if necessary.
80  */
81 
82 /*
83  * check if another process is holding lock on the lock file.
84  *
85  * return: 0 if file is not locked, else,
86  *	   1 if file is locked by another process, else,
87  *	   -1 on any error.
88  */
89 int
90 _check_daemon_lock(const char *name)
91 {
92 	int		fd, err;
93 	struct flock	lock;
94 
95 	if ((fd = open_daemon_lock(name, O_RDONLY)) == -1) {
96 		if (errno == ENOENT)
97 			return (0);
98 		return (-1);
99 	}
100 
101 	lock.l_type = F_WRLCK;
102 	lock.l_whence = SEEK_SET;
103 	lock.l_start = (off_t)0;
104 	lock.l_len = (off_t)0;
105 
106 	err = fcntl(fd, F_GETLK, &lock);
107 	(void) close(fd);
108 
109 	if (err == -1)
110 		return (-1);
111 
112 	return ((lock.l_type == F_UNLCK) ? 0 : 1);
113 }
114 
115 static int
116 open_daemon_lock(const char *name, int mode)
117 {
118 	char		lock_file[MAXPATHLEN], buf[MAXPATHLEN];
119 	int		fd;
120 	char		*p;
121 
122 	/*
123 	 * Our args look like this:
124 	 *   svc:/network/nfs/status:default
125 	 * We want to create a lock file named like this:
126 	 *   /etc/svc/volatile/nfs-status.lock
127 	 * i.e., we want the last two path components in the name.
128 	 */
129 	(void) strncpy(buf, name, MAXPATHLEN);
130 
131 	/* First, strip off ":<instance>", if present. */
132 	p = strrchr(buf, ':');
133 	if (p != NULL)
134 		*p = '\0';
135 
136 	/* Next, find final '/' and replace it with a dash */
137 	p = strrchr(buf, '/');
138 	if (p == NULL)
139 		p = buf;
140 	else {
141 		*p = '-';
142 		/* Now find the start of what we want our name to be */
143 		p = strrchr(buf, '/');
144 		if (p == NULL)
145 			p = buf;
146 		else
147 			p++;
148 	}
149 
150 	(void) snprintf(lock_file, MAXPATHLEN, "/etc/svc/volatile/%s.lock", p);
151 
152 	if ((fd = open(lock_file, mode, 0644)) == -1)
153 		return (-1);
154 
155 	if (mode & O_CREAT)
156 		(void) fchmod(fd, 0644);
157 
158 	return (fd);
159 }
160 /*
161  * lock the file, write caller's pid to the lock file
162  * return: 0 if caller can establish lock, else,
163  *	   pid of the current lock holder, else,
164  *	   -1 on any printable error.
165  */
166 pid_t
167 _enter_daemon_lock(const char *name)
168 {
169 	int		fd;
170 	pid_t		pid;
171 	char		line[BUFSIZ];
172 	struct flock	lock;
173 
174 	pid = getpid();
175 	(void) snprintf(line, sizeof (line), "%ld\n", pid);
176 
177 	if ((fd = open_daemon_lock(name, O_RDWR|O_CREAT)) == -1)
178 		return ((pid_t)-1);
179 
180 	lock.l_type = F_WRLCK;
181 	lock.l_whence = SEEK_SET;
182 	lock.l_start = (off_t)0;
183 	lock.l_len = (off_t)0;
184 
185 	if (fcntl(fd, F_SETLK, &lock) == -1) {
186 		if (fcntl(fd, F_GETLK, &lock) == -1) {
187 			(void) close(fd);
188 			return ((pid_t)-1);
189 		}
190 		(void) close(fd);
191 		return (lock.l_pid);
192 	}
193 
194 	if (write(fd, line, strlen(line)) == -1) {
195 		(void) close(fd);
196 		return ((pid_t)-1);
197 	}
198 
199 	return ((pid_t)0);
200 }
201 
202 int
203 _create_daemon_lock(const char *name, uid_t uid, gid_t gid)
204 {
205 	int fd = open_daemon_lock(name, O_CREAT);
206 	int ret;
207 
208 	if (fd < 0)
209 		return (-1);
210 
211 	ret = fchown(fd, uid, gid);
212 	(void) close(fd);
213 
214 	return (ret);
215 }
216 
217 /*
218  * Check the "application/auto_enable" property for the passed FMRI.
219  * scf_simple_prop_get() should find the property on an instance
220  * or on the service FMRI.  The routine returns:
221  * -1: inconclusive (likely no such property or FMRI)
222  *  0: auto_enable is false
223  *  1: auto_enable is true
224  */
225 int
226 is_auto_enabled(char *fmri)
227 {
228 	scf_simple_prop_t *prop;
229 	int retval = -1;
230 	uint8_t *ret;
231 
232 	prop = scf_simple_prop_get(NULL, fmri, "application", "auto_enable");
233 	if (!prop)
234 		return (retval);
235 	ret = scf_simple_prop_next_boolean(prop);
236 	retval = (*ret != 0);
237 	scf_simple_prop_free(prop);
238 	return (retval);
239 }
240