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