1 /*
2  * The contents of this file are subject to the terms of the
3  * Common Development and Distribution License, Version 1.0 only
4  * (the "License").  You may not use this file except in compliance
5  * with the License.
6  *
7  * See the file CDDL.Schily.txt in this distribution for details.
8  *
9  * When distributing Covered Code, include this CDDL HEADER in each
10  * file and include the License file CDDL.Schily.txt from this distribution.
11  */
12 /*
13  *	Lock the project via the changeset file in lock:
14  *	$SET_HOME/.sccs/SCCS/z.changeset
15  *
16  * @(#)lockchset.c	1.6 20/08/10 Copyright 2020 J. Schilling
17  */
18 #if defined(sun)
19 #pragma ident "@(#)lockchset.c	1.6 20/08/10 Copyright 2020 J. Schilling"
20 #endif
21 
22 #if defined(sun)
23 #pragma ident	"@(#)lockchset.c"
24 #pragma ident	"@(#)sccs:lib/comobj/lockchset.c"
25 #endif
26 #include	<defines.h>
27 #include	<schily/signal.h>
28 
29 LOCAL	RETSIGTYPE	siglock	__PR((int));
30 
31 int
lockchset(ppid,pid,uuname)32 lockchset(ppid, pid, uuname)
33 	pid_t	ppid;			/* The pid of the parent process.    */
34 	pid_t	pid;			/* The pid of the holding process.   */
35 	char	*uuname;		/* The hostname for this machine.    */
36 {
37 	char	*cs;
38 	int	r = 0;
39 
40 	if (changesetfile == NULL)
41 		return (0);
42 
43 	/*
44 	 * Apply for a global lock...
45 	 * First check whether there is a lock owned by our parent
46 	 * that may be sccs(1). If the lock is present and owned
47 	 * by our parent, everything is OK since we are called by
48 	 * sccs(1) for a larger work.
49 	 *
50 	 * If ppid = 0, this is sccs(1) and we do not check for a lock
51 	 * hold by the parent.
52 	 */
53 	cs = auxf(changesetfile, 'z');
54 	if (ppid != 0 && (r = ismylock(cs, ppid, uuname)) > 0)
55 		return (0);
56 
57 	/*
58 	 * Since r is pre-initialized as 0, we assume a crash in case that the
59 	 * non-empty global lock file exists and we did not check for a parent
60 	 * owned lock because we are sccs(1).
61 	 */
62 	if (r == 0 && exists(cs) && Statbuf.st_size > 0) {
63 		/*
64 		 * A lock file exists that does not match our
65 		 * parent. A previous action may have crashed.
66 		 * So we need to run a recovery protocol. In
67 		 * order to learn what we need to do, we abort
68 		 * for now to find out when it happens.
69 		 */
70 		char	ebuf[MAXPATHLEN];
71 
72 		snprintf(ebuf, sizeof (ebuf), gettext(
73 		    "Recovery needed? Otherwise remove '%s'"), cs);
74 		fatal(ebuf);
75 	}
76 	/*
77 	 * Lock out any other user who may be trying to
78 	 * process files from the same project.
79 	 */
80 	return (lockit(cs, SCCS_LOCK_ATTEMPTS, pid, uuname));
81 }
82 
83 int
unlockchset(pid,uuname)84 unlockchset(pid, uuname)
85 	pid_t	pid;			/* The pid of the holding process.   */
86 	char	*uuname;		/* The hostname for this machine.    */
87 {
88 	if (changesetfile == NULL)
89 		return (0);
90 	return (unlockit(auxf(changesetfile, 'z'), pid, uuname));
91 }
92 
93 /*
94  * Return whether a lockfile name is pointing to the active global lock which
95  * is the lock file for the changeset file. We need to do this check in order
96  * to avoid a deadlock from attempting to create the changeset lock a second
97  * time while trying to modify the changeset file from one of the low level
98  * programs when updating the global status.
99  *
100  * Returns:
101  *
102  *	0	A changeset lock is not active of "fname" is not thename for
103  *		the changeset lock file.
104  *
105  *	1	"fname" points to the active changeset lock file.
106  */
107 int
islockchset(fname)108 islockchset(fname)
109 	char	*fname;			/* The filename to check against    */
110 {
111 	char		*p;
112 	struct stat	sbchset;
113 	struct stat	sb;
114 
115 	if (changesetfile == NULL)	/* Cannot be the changeset lock name */
116 		return (0);
117 
118 	if (!SETHOME_CHSET())		/* Not in a changeset environment    */
119 		return (0);
120 
121 	if ((p = strrchr(fname, '/')) == NULL)
122 		p = fname;
123 	else
124 		p++;
125 	if (strcmp(p, "z.changeset"))	/* Wrong name, cannot be changeset   */
126 		return (0);
127 
128 	if (stat(auxf(changesetfile, 'z'), &sbchset) < 0)
129 		return (0);		/* Currently no changeset lock file */
130 
131 	if (stat(fname, &sb) < 0)	/* Cannot be identical to chset lock */
132 		return (0);
133 
134 	if (sbchset.st_dev != sb.st_dev)
135 		return (0);
136 	if (sbchset.st_ino != sb.st_ino)
137 		return (0);
138 
139 	return (1);
140 }
141 
142 int
refreshchsetlock()143 refreshchsetlock()
144 {
145 	if (changesetfile == NULL)
146 		return (0);
147 	return (lockrefresh(auxf(changesetfile, 'z')));
148 }
149 
150 LOCAL	char	*lockfile;		/* Name of current file lock */
151 
152 #if	defined(HAVE_ALARM)
153 #if	(defined(HAVE_SIGPROCMASK) && defined(SA_RESTART)) || \
154 	defined(HAVE_SIGSETMASK)
155 
156 LOCAL RETSIGTYPE
siglock(signo)157 siglock(signo)
158 	int	signo;
159 {
160 	if (changesetfile)
161 		refreshchsetlock();	/* Refresh Changeset lock */
162 	if (lockfile && *lockfile)
163 		lockrefresh(lockfile);	/* Refresh lock for current file */
164 
165 	alarm(30);
166 }
167 #endif
168 #endif
169 
170 void
timersetlockfile(name)171 timersetlockfile(name)
172 	char	*name;
173 {
174 	lockfile = name;
175 }
176 
177 /*
178  * Set a repeated timer for every 30 seconds to refresh the lock files.
179  * This avoids locks over NFS to expire in case that we are working on a
180  * longer lasting task.
181  *
182  * We can do this only on platforms that support signals that do not
183  * interrupt system calls. So we need either POSIX or BSD signals.
184  */
185 void
timerchsetlock()186 timerchsetlock()
187 {
188 #if	defined(HAVE_ALARM)
189 #if	defined(HAVE_SIGPROCMASK) && defined(SA_RESTART)
190 	struct sigaction sa;
191 
192 	sigemptyset(&sa.sa_mask);
193 	sa.sa_handler = siglock;
194 	sa.sa_flags = SA_RESTART;
195 	(void) sigaction(SIGALRM, &sa, (struct sigaction *)0);
196 	alarm(30);
197 #else
198 #ifdef	HAVE_SIGSETMASK
199 	struct sigvec	sv;
200 
201 	sv.sv_mask = 0;
202 	sv.sv_handler = siglock;
203 	sv.sv_flags = 0;
204 	(void) sigvec(SIGALRM, &sv, (struct sigvec *)0);
205 	alarm(30);
206 #endif
207 #endif
208 #endif
209 }
210