summaryrefslogtreecommitdiffstats
path: root/lib/stonith/st_ttylock.c
blob: adc918df19d22fdf8394374deeb2f59e651252f2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
/*
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <lha_internal.h>

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include <clplumbing/cl_signal.h>
#include <stonith/st_ttylock.h>

/*
 * The following information is from the Filesystem Hierarchy Standard
 * version 2.1 dated 12 April, 2000.
 *
 * 5.6 /var/lock : Lock files
 * Lock files should be stored within the /var/lock directory structure.
 * Device lock files, such as the serial device lock files that were originally
 * found in either /usr/spool/locks or /usr/spool/uucp, must now be stored in
 * /var/lock. The naming convention which must be used is LCK.. followed by
 * the base name of the device file. For example, to lock /dev/cua0 the file
 * LCK..cua0 would be created.
 * 
 * The format used for device lock files must be the HDB UUCP lock file format.
 * The HDB format is to store the process identifier (PID) as a ten byte
 * ASCII decimal number, with a trailing newline. For example, if process 1230
 * holds a lock file, it would contain the eleven characters: space, space,
 * space, space, space, space, one, two, three, zero, and newline.
 * Then, anything wishing to use /dev/cua0 can read the lock file and act
 * accordingly (all locks in /var/lock should be world-readable).
 *
 *
 * PERMISSIONS NOTE:
 * Different linux distributions set the mode of the lock directory differently
 * Any process which wants to create lock files must have write permissions
 * on HA_VARLOCKDIR (probably /var/lock).  For things like the heartbeat API
 * code, this may mean allowing the uid of the processes that use this API
 * to join group uucp, or making the binaries setgid to uucp.
 */

#define	DEVDIR	"/dev/"
#define	DEVLEN	(sizeof(DEVDIR)-1)

static void raw_device (const char *dev, char *dest_name, size_t size);
static int DoLock(const char * prefix, const char *lockname);
static int DoUnlock(const char * prefix, const char *lockname);

/* The code in this file originally written by Guenther Thomsen */
/* Somewhat mangled by Alan Robertson */

/*
 * Lock a tty (using lock files, see linux `man 2 open` close to O_EXCL) 
 * serial_device has to be _the complete path_, i.e. including '/dev/' to the
 * special file, which denotes the tty to lock -tho
 * return 0 on success, 
 * -1 if device is locked (lockfile exists and isn't stale),
 * -2 for temporarily failure, try again,
 * other negative value, if something unexpected happend (failure anyway)
 */

static void
raw_device (const char *serial_device, char *dest_name, size_t size)
{
	char*		dp	= dest_name;
	const char*	sp	= serial_device+DEVLEN;
	const char* 	dpend	= dp + size - 1;

	while (*sp != '\0' && dp < dpend) {
		if (isalnum((unsigned int)*sp))
			*dp++ = *sp;
		sp++;
	}
	*dp = EOS;
}

int
st_ttylock(const char *serial_device)
{
	char rawname[64];

	if (serial_device == NULL) {
		errno = EFAULT;
		return -3;
	}
	raw_device (serial_device, rawname, sizeof(rawname));
	return(DoLock("LCK..", rawname));
}

/*
 * Unlock a tty (remove its lockfile) 
 * do we need to check, if its (still) ours? No, IMHO, if someone else
 * locked our line, it's his fault  -tho
 * returns 0 on success
 * <0 if some failure occured
 */ 

int
st_ttyunlock(const char *serial_device)
{
	char rawname[64];

	if (serial_device == NULL) {
		errno = EFAULT;
		return -3;
	}

	raw_device (serial_device, rawname, sizeof(rawname));
	return(DoUnlock("LCK..", rawname));
}

/* This is what the FHS standard specifies for the size of our lock file */
#define	LOCKSTRLEN	11

static int
DoLock(const char * prefix, const char *lockname)
{
	char lf_name[256], tf_name[256], buf[LOCKSTRLEN+1];
	int fd;
	long pid, mypid;
	int rc;
	struct stat sbuf;

	mypid = (unsigned long) getpid();

	snprintf(lf_name, sizeof(lf_name), "%s/%s%s"
	,	HA_VARLOCKDIR, prefix, lockname);

	snprintf(tf_name, sizeof(tf_name), "%s/tmp%lu-%s"
	,	HA_VARLOCKDIR, mypid, lockname);

	if ((fd = open(lf_name, O_RDONLY)) >= 0) {
		if (fstat(fd, &sbuf) >= 0 && sbuf.st_size < LOCKSTRLEN) {
			sleep(1); /* if someone was about to create one,
			   	   * give'm a sec to do so
				   * Though if they follow our protocol,
				   * this won't happen.  They should really
				   * put the pid in, then link, not the
				   * other way around.
				   */
		}
		if (read(fd, buf, sizeof(buf)) < 1) {
			/* lockfile empty -> rm it and go on */;
		} else {
			if (sscanf(buf, "%lu", &pid) < 1) {
				/* lockfile screwed up -> rm it and go on */
			} else {
				if (pid > 1 && ((long)getpid() != pid)
				    &&	((CL_KILL((pid_t)pid, 0) >= 0)
					 ||	errno != ESRCH)) {
					/* tty is locked by existing (not
					 * necessarily running) process
					 * -> give up */
					close(fd);
					return -1;
				} else {
					/* stale lockfile -> rm it and go on */
				}
			}
		}
		unlink(lf_name);
	}
	if ((fd = open(tf_name, O_CREAT | O_WRONLY | O_EXCL, 0644)) < 0) {
		/* Hmmh, why did we fail? Anyway, nothing we can do about it */
		return -3;
	}

	/* Slight overkill with the %*d format ;-) */
	snprintf(buf, sizeof(buf), "%*lu\n", LOCKSTRLEN-1, mypid);

	if (write(fd, buf, LOCKSTRLEN) != LOCKSTRLEN) {
		/* Again, nothing we can do about this */
		return -3;
	}
	close(fd);

	switch (link(tf_name, lf_name)) {
	case 0:
		if (stat(tf_name, &sbuf) < 0) {
			/* something weird happened */
			rc = -3;
			break;
		}
		if (sbuf.st_nlink < 2) {
			/* somehow, it didn't get through - NFS trouble? */
			rc = -2;
			break;
		}
		rc = 0;
		break;
	case EEXIST:
		rc = -1;
		break;
	default:
		rc = -3;
	}
	unlink(tf_name);
	return rc;
}

static int
DoUnlock(const char * prefix, const char *lockname)
{
	char lf_name[256];
	
	snprintf(lf_name, sizeof(lf_name), "%s/%s%s", HA_VARLOCKDIR
	,	prefix, lockname);
	return unlink(lf_name);
}