summaryrefslogtreecommitdiff
path: root/src/pidfile.c
blob: 07f7521d03adce187a620fcc1f1abf0f6c21d128 (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
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
#include "port/stdio.h"			/* for snprintf */
#include "port/string.h"		/* for strdup, strcspn */
#include "port/stdbool.h"		/* for bool */
#include "port/unistd.h"		/* for getpid, open, write, read,
					 * unlink, lockf */

#include "pidfile.h"
#include "log.h"
#include "errors.h"

#include <stdlib.h>			/* for strtol */
#include <errno.h>			/* for errno */
#include <sys/types.h>			/* for pid_t, ssize_t, off_t */
#include <sys/stat.h>			/* for umask */
#include <fcntl.h>			/* for O_RDWR */
#include <signal.h>			/* for kill */

/* abstraction of the pid file handling in order to unclutter the
 * daemon start and stop function
 */

#define VAR_RUN_DIR "/var/run"

void pidfile_init( struct pidfile_t *pidfile ) {
	pidfile->filename[0] = '\0';
	pidfile->fd = -1;
	pidfile->locked = false;
	pidfile->running = false;
}

void pidfile_set_from_daemon_name( struct pidfile_t *pidfile, const char *daemon ) {
	pidfile_init( pidfile );
	snprintf( pidfile->filename, PATH_MAX, "%s/%s.pid", VAR_RUN_DIR, daemon );
}

void pidfile_set_from_filename( struct pidfile_t *pidfile, const char *filename ) {
	pidfile_init( pidfile );
	/* make sure the filename is shorter than the POSIX.1 length */
	snprintf( pidfile->filename, PATH_MAX, "%s", filename );
}

bool is_daemon_running( struct pidfile_t *pidfile, pid_t *pid, error_t *error ) {
	int res;
	ssize_t bytes_read;
	char buf[256];
	char *end_ptr;
	
	/* assume daemon is not running */
	pidfile->running = false;
	
	/* open pidfile with correct permissions */
	pidfile->fd = open( pidfile->filename, O_RDWR, 0644 );
	if( pidfile->fd < 0 ) {
		if( errno == ENOENT ) {
			/* this is good, pid file doesn't exist at all */
			LOG( LOG_DEBUG, "No pidfile '%s' found, daemon is not running", pidfile->filename );
			(void)close( pidfile->fd );
			*error = OK;
			return pidfile->running;
		} else {
			LOG( LOG_EMERG, "Unable to open pidfile '%s' for reading: %s", pidfile->filename, strerror( errno ) );
			*error = ERR_INTERNAL;
			return pidfile->running;
		}
	}
	
	/* try to lock the pid file (non-blocking) */
	res = lockf( pidfile->fd, F_TLOCK, (off_t)0 );
	if( res < 0 ) {
		if( errno == EAGAIN ) {
			/* another process locks the file already */
			LOG( LOG_DEBUG, "Another process locks the pidfile, daemon already running" );
			*error = OK;
			pidfile->locked = true;
			pidfile->running = true;
		} else {
			LOG( LOG_EMERG, "Unable to lock pidfile '%s': %s", pidfile->filename, strerror( errno ) );
			(void)close( pidfile->fd );
			*error = ERR_INTERNAL;
			pidfile->running = false;
			return pidfile->running;
		}
	} else {
		pidfile->locked = false;
	}
	
	/* try to read the pid from the file */
	bytes_read = read( pidfile->fd, buf, sizeof( buf ) - 1 );
	if( bytes_read < 0 ) {
		LOG( LOG_EMERG, "Unable to read pid from pidfile '%s': %s", pidfile->filename, strerror( errno ) );
		(void)close( pidfile->fd );
		*error = ERR_INTERNAL;
		return pidfile->running;
	}
	
	/* parse the file and see if we have a valid pid on the first line */
	buf[bytes_read] = 0;
	buf[strcspn( buf, "\n" )] = 0;
	
	errno = 0;
	*pid = (pid_t)strtol( buf, &end_ptr, 10 );
	if( ( errno != 0 ) /* ERANGE or valid base */ ||
	    ( end_ptr == NULL ) /* pre-condition for check for '\0'! */ ||
	    ( end_ptr - buf < 1 ) /* too short */ ||
	    ( *pid < 2 ) /* too small value */ ) {
		LOG( LOG_EMERG, "pidfile '%s' contains invalid data, can't read PID from it!", pidfile->filename );
		(void)close( pidfile->fd );
		*error = ERR_INTERNAL;
		return pidfile->running;
	}
	LOG( LOG_DEBUG, "Found PID '%lu' in pidfile", *pid );
	
	/* the pid is valid, but is there a process with the pid actually running?
	 * (this handles the case of kill -9!)
	 */
	res = kill( *pid, 0 );
	if( res < 0 ) {
		if( errno == ESRCH ) {
			/* this is fine, process doesn't exist with this PID */
			LOG( LOG_EMERG, "Found PID '%lu' in pidfile '%s', but no such process is running. Check and manually delete the pidfile!", *pid, pidfile->filename );
			(void)close( pidfile->fd );
			*error = ERR_INTERNAL;
			return pidfile->running;
		} else {
			LOG( LOG_EMERG, "Can't check if processor with PID '%lu' is alive: %s", *pid, strerror( errno ) );
			(void)close( pidfile->fd );
			*error = ERR_INTERNAL;
			return pidfile->running;
		}
	}
	LOG( LOG_DEBUG, "A process with PID '%lu' is already running", *pid );

	/* process successfuly signaled, so a process with this PID exists
	 * (worst case, we assume, it's the daemon)
	 */
	(void)close( pidfile->fd );
	*error = OK;
	return pidfile->running;
}

error_t pidfile_create( struct pidfile_t *pidfile ) {
	int res;
	char pid_string[20];
	ssize_t bytes_writen;
	
	/* create or open pid file with correct permissions */
	pidfile->fd = open( pidfile->filename, O_CREAT | O_WRONLY | O_EXCL, 0644 );
	if( pidfile->fd < 0 ) {
		LOG( LOG_EMERG, "Unable to open pidfile '%s' for writing: %s", pidfile->filename, strerror( errno ) );
		return ERR_INTERNAL;
	}
	
	/* Try to lock the pid file (non-blocking) */
	res = lockf( pidfile->fd, F_TLOCK, (off_t)0 );
	if( res < 0 ) {
		if( errno == EAGAIN ) {
			/* another process locks the file already, this should not happen, maybe a
			 * race between to daemons started in parallel?
			 */
			LOG( LOG_EMERG, "Unable to lock pidfile '%s' after creation, daemon started in parallel?", pidfile->filename );
			(void)close( pidfile->fd );
			(void)unlink( pidfile->filename );
			return ERR_INVALID_STATE;
		} else {
			LOG( LOG_EMERG, "Unable to lock pidfile '%s' after creation: %s", pidfile->filename, strerror( errno ) );
			(void)close( pidfile->fd );
			(void)unlink( pidfile->filename );
			return ERR_INTERNAL;
		}
	}

	/* Truncate the contents of the file, so we don't get funny values
	 * in the pid file
	 */
	if( ftruncate( pidfile->fd, (off_t)0 ) < 0 ) {
		LOG( LOG_EMERG, "Unable to truncate the pidfile '%s' before writing to it", pidfile->filename, strerror( errno ) );
		(void)close( pidfile->fd );
		(void)unlink( pidfile->filename );
		return ERR_INTERNAL;
	}

	/* We remember the pid in the file for init scripts which rely on the pid
	 * to be stored here.
	 */
	snprintf( pid_string, 20, "%lu\n", (unsigned long)getpid( ) );
	bytes_writen = write( pidfile->fd, pid_string, strlen( pid_string ) );
	if( bytes_writen < 0 ) {
		LOG( LOG_EMERG, "Unable to write PID into the pidfile '%s': %s", pidfile->filename, strerror( errno ) );
		(void)close( pidfile->fd );
		(void)unlink( pidfile->filename );
		return ERR_INTERNAL;
	} else if( bytes_writen != (ssize_t)strlen( pid_string ) ) {
		/* non-atomic write on files with so little data, strange, should never happen! */
		LOG( LOG_EMERG, "Non-atomic write failed when storing the PID into the pidfile '%s'", pidfile->filename );
	}
	LOG( LOG_DEBUG, "Stored '%lu' into the pidfile '%s' and locked it", (unsigned long)getpid( ), pidfile->filename );
	
	pidfile->locked = true;
	
	return OK;
}

error_t pidfile_release( struct pidfile_t *pidfile ) {
	error_t error = OK;

	LOG( LOG_DEBUG, "Releasing (unlocking/closing) pidfile '%s' (fd: %d, locked: %d)",
		pidfile->filename, pidfile->fd, pidfile->locked );

	if( pidfile->locked ) {
		if( lockf( pidfile->fd, F_ULOCK, (off_t)0 ) < 0 ) {
			LOG( LOG_ALERT, "Unable to unlock the pidfile '%s': %s (%d)",
				pidfile->filename, strerror( errno ), errno );
			error = ERR_INTERNAL;
		}
	}
	
	if( pidfile->fd >= 0 ) {
		if( close( pidfile->fd ) < 0 ) {
			LOG( LOG_ALERT, "Unable to close the pidfile '%s': %s (%d)",
				pidfile->filename, strerror( errno ), errno );
			error = ERR_INTERNAL;
		}
		pidfile->fd = -1;
	}

	return error;
}

error_t pidfile_remove( struct pidfile_t *pidfile ) {
	error_t error = OK;

	LOG( LOG_DEBUG, "Removing pidfile '%s' (fd: %d, locked: %d, running: %d)",
		pidfile->filename, pidfile->fd, pidfile->locked, pidfile->running );


	if( !pidfile->running && pidfile->fd != -1 ) {
		if( unlink( pidfile->filename ) < 0 ) {
			LOG( LOG_ALERT, "Unable to remove the pidfile '%s': %s (%d)",
				pidfile->filename, strerror( errno ), errno );
			error = ERR_INTERNAL;
		}
	}

	return error;
}