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