xref: /illumos-gate/usr/src/cmd/svr4pkg/libinst/lockinst.c (revision 4eaa471005973e11a6110b69fe990530b3b95a38)
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 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29 
30 
31 #include <stdio.h>
32 #include <limits.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <fcntl.h>
36 #include <errno.h>
37 #include <pkglocs.h>
38 #include <locale.h>
39 #include <libintl.h>
40 #include <string.h>
41 #include <signal.h>
42 #include <sys/types.h>
43 #include <sys/signal.h>
44 #include <sys/fault.h>
45 #include <sys/syscall.h>
46 #include <pkglib.h>
47 #include "libadm.h"
48 
49 extern int errno;
50 
51 #define	ST_QUIT	1
52 #define	ST_OK	0
53 
54 #define	LOCKFILE	".lockfile"
55 #define	LCKBUFSIZ	128
56 #define	LOCKWAIT	20	/* seconds between retries */
57 #define	LOCKRETRY	10	/* number of retries for a DB lock */
58 #define	LF_SIZE		128	/* size of governing lock file */
59 
60 #define	MSG_WTING	"NOTE: Waiting for access to the package database."
61 #define	MSG_XWTING	"NOTE: Waiting for exclusive access to the package " \
62 			    "database."
63 #define	MSG_WTFOR	"NOTE: Waiting for %s of %s to complete."
64 #define	WRN_CLRLOCK	"WARNING: Stale lock installed for %s, pkg %s quit " \
65 			    "in %s state."
66 #define	WRN_CLRLOCK1	"Removing lock."
67 #define	ERR_MKLOCK	"unable to create governing lock file <%s>."
68 #define	ERR_NOLOCK	"unable to install governing lock on <%s>."
69 #define	ERR_NOOPEN	"unable to open <%s>."
70 #define	ERR_LCKTBL	"unable to lock <%s> - lock table full."
71 #define	ERR_LCKREM	"unable to lock <%s> - remote host unavailable."
72 #define	ERR_BADLCK	"unable to lock <%s> - unknown error."
73 #define	ERR_DEADLCK	"unable to lock <%s> - deadlock condition."
74 
75 static pid_t lock_pid;
76 static int lock_fd, lock_is_applied;
77 static char lock_name[PKGSIZ];
78 static char lock_pkg[PKGSIZ];
79 static char lock_place[PKGSIZ];
80 static unsigned int lock_state;
81 static char lockbuf[LCKBUFSIZ];
82 static char lockpath[PATH_MAX];
83 
84 #define	LOCK_NAME_OLD_PKG	"old version pkg command"
85 #define	LOCK_PKG_UNKNOWN	"unknown package"
86 #define	LOCK_PLACE_UNKNOWN	"unknown"
87 
88 /*
89  * This function writes the PID, effective utility name, package name,
90  * current progress of the utility and the exit state to the lockfile in
91  * support of post mortem operations.
92  */
93 static int
94 wrlockdata(int fd, int this_pid, char *this_name,
95     char *this_pkg, char *this_place, unsigned int this_state)
96 {
97 	if (this_pid < 0 || *this_name == '\000')
98 		return (0);
99 
100 	(void) memset(lockbuf, 0, LCKBUFSIZ);
101 
102 	(void) snprintf(lockbuf, sizeof (lockbuf),
103 			"%d %s %s %s %d\n", this_pid, this_name, this_pkg,
104 			this_place, this_state);
105 
106 	(void) lseek(fd, 0, SEEK_SET);
107 	if (write(fd, lockbuf, LF_SIZE) == LF_SIZE)
108 		return (1);
109 	else
110 		return (0);
111 }
112 
113 /*
114  * This function reads the lockfile to obtain the PID and name of the lock
115  * holder. Upon those rare circumstances that an older version of pkgadd
116  * created the lock, this detects that zero-length file and provides the
117  * appropriate data. Since this data is only used if an actual lock (from
118  * lockf()) is detected, a manually created .lockfile will not result in a
119  * message.
120  */
121 static void
122 rdlockdata(int fd)
123 {
124 	(void) lseek(fd, 0, SEEK_SET);
125 	if (read(fd, lockbuf, LF_SIZE) != LF_SIZE) {
126 		lock_pid = 0;
127 		(void) strlcpy(lock_name, LOCK_NAME_OLD_PKG,
128 						sizeof (lock_name));
129 
130 		(void) strlcpy(lock_pkg, LOCK_PKG_UNKNOWN,
131 						sizeof (lock_pkg));
132 
133 		(void) strlcpy(lock_place, LOCK_PLACE_UNKNOWN,
134 						sizeof (lock_place));
135 
136 		lock_state = ST_OK;
137 	} else {
138 		/* LINTED format argument contains unbounded string specifier */
139 		(void) sscanf(lockbuf, "%ld %s %s %s %u", &lock_pid,
140 			lock_name, lock_pkg, lock_place, &lock_state);
141 	}
142 }
143 
144 static void
145 do_alarm(int n)
146 {
147 #ifdef lint
148 	int i = n;
149 	n = i;
150 #endif	/* lint */
151 	(void) signal(SIGALRM, do_alarm);
152 	(void) alarm(LOCKWAIT);
153 }
154 
155 /*
156  * This establishes a locked status file for a pkgadd, pkgrm or swmtool - any
157  * of the complex package processes. Since numerous packages currently use
158  * installf and removef in preinstall scripts, we can't enforce a contents
159  * file write lock throughout the install process. In 2.7 we will enforce the
160  * write lock and allow this lock to serve as a simple information carrier
161  * which can be used by installf and removef too.
162  * Arguments:
163  *  util_name - the name of the utility that is claiming the lock
164  *  pkg_name - the package that is being locked (or "all package")
165  *  place - a string of ascii characters that defines the initial "place" where
166  *    the current operation is - this is updated by lockupd() and is a string
167  *    is used fr post mortem operations if the utility should quit improperly.
168  * Returns (int):
169  *  == 0 - failure
170  *  != 0 - success
171  */
172 
173 int
174 lockinst(char *util_name, char *pkg_name, char *place)
175 {
176 	int	fd, retry_cnt;
177 
178 	/* assume "initial" if no "place" during processing specified */
179 
180 	if ((place == (char *)NULL) || (*place == '\0')) {
181 		place = "initial";
182 	}
183 
184 	(void) snprintf(lockpath, sizeof (lockpath),
185 			"%s/%s", get_PKGADM(), LOCKFILE);
186 
187 	/* If the exit file is not present, create it. */
188 	/* LINTED O_CREAT without O_EXCL specified in call to open() */
189 	if ((fd = open(lockpath, O_RDWR | O_CREAT, 0600)) == -1) {
190 		progerr(gettext(ERR_MKLOCK), lockpath);
191 		return (0);
192 	}
193 
194 	lock_fd = fd;
195 
196 	retry_cnt = LOCKRETRY;
197 	lock_is_applied = 0;
198 
199 	(void) signal(SIGALRM, do_alarm);
200 	(void) alarm(LOCKWAIT);
201 
202 	/*
203 	 * This tries to create the lock LOCKRETRY times waiting LOCKWAIT
204 	 * seconds between retries.
205 	 */
206 	do {
207 
208 		if (lockf(fd, F_LOCK, 0)) {
209 			/*
210 			 * Try to read the status of the last (or current)
211 			 * utility.
212 			 */
213 			rdlockdata(fd);
214 
215 			logerr(gettext(MSG_WTFOR), lock_name, lock_pkg);
216 		} else {	/* This process has the lock. */
217 			rdlockdata(fd);
218 
219 			if (lock_state != 0) {
220 				logerr(gettext(WRN_CLRLOCK), lock_name,
221 				    lock_pkg, lock_place);
222 				logerr(gettext(WRN_CLRLOCK1));
223 			}
224 
225 			lock_pid = getpid();
226 			(void) strlcpy(lock_name, (util_name) ?
227 			    util_name : gettext("unknown"), sizeof (lock_name));
228 
229 			(void) strlcpy(lock_pkg, (pkg_name) ?
230 			    pkg_name : gettext("unknown"), sizeof (lock_pkg));
231 
232 			(void) wrlockdata(fd, lock_pid, lock_name,
233 			    lock_pkg, place, ST_QUIT);
234 			lock_is_applied = 1;
235 			break;
236 		}
237 	} while (retry_cnt--);
238 
239 	(void) signal(SIGALRM, SIG_IGN);
240 
241 	if (!lock_is_applied) {
242 		progerr(gettext(ERR_NOLOCK), lockpath);
243 		return (0);
244 	}
245 
246 	return (1);
247 }
248 
249 /*
250  * This function updates the utility progress data in the lock file. It is
251  * used for post mortem operations if the utility should quit improperly.
252  */
253 void
254 lockupd(char *place)
255 {
256 	(void) wrlockdata(lock_fd, lock_pid, lock_name, lock_pkg, place,
257 			ST_QUIT);
258 }
259 
260 /*
261  * This clears the governing lock and closes the lock file. If this was
262  * called already, it just returns.
263  */
264 void
265 unlockinst(void)
266 {
267 	if (lock_is_applied) {
268 		(void) wrlockdata(lock_fd, lock_pid, lock_name, lock_pkg,
269 			"finished", ST_OK);
270 
271 		/*
272 		 * If close() fails, we can't be sure the lock has been
273 		 * removed, so we assume the worst in case this function is
274 		 * called again.
275 		 */
276 		if (close(lock_fd) != -1)
277 			lock_is_applied = 0;
278 	}
279 }
280