lockinst.c revision 9781:ccf49524d5dc
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
49extern 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
75static pid_t lock_pid;
76static int lock_fd, lock_is_applied;
77static char lock_name[PKGSIZ];
78static char lock_pkg[PKGSIZ];
79static char lock_place[PKGSIZ];
80static unsigned int lock_state;
81static char lockbuf[LCKBUFSIZ];
82static 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 */
93static int
94wrlockdata(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 */
121static void
122rdlockdata(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
144static void
145do_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
173int
174lockinst(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 */
253void
254lockupd(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 */
264void
265unlockinst(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