1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2004-2021 Free Software Foundation, Inc.
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 3 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General
15 Public License along with this library. If not, see
16 <http://www.gnu.org/licenses/>. */
17
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <string.h>
29
30 #include <mailutils/daemon.h>
31 #include <mailutils/errno.h>
32 #include <mailutils/error.h>
33 #include <mailutils/nls.h>
34
35 static char *pidfile;
36 static pid_t current_pid;
37
38 /* Return 0 if DIR is writable for EUID/EGID.
39 Otherwise, return error code. */
40 static int
ewraccess(const char * dir)41 ewraccess (const char *dir)
42 {
43 struct stat st;
44 if (stat (dir, &st))
45 return errno;
46 if ((st.st_mode & S_IWOTH)
47 || (st.st_gid == getegid () && (st.st_mode & S_IWGRP))
48 || (st.st_uid == geteuid () && (st.st_mode & S_IWUSR)))
49 return 0;
50 else
51 return EACCES;
52 }
53
54 /* Return 0 if DIR is writable. If necessary and possible, raise to
55 EUID 0, in that case return prior EUID in the memory location pointed to
56 by PUID. */
57 static int
access_dir(const char * dir,uid_t * puid)58 access_dir (const char *dir, uid_t *puid)
59 {
60 int ec = ewraccess (dir);
61 if (ec)
62 {
63 if (ec == EACCES && access (dir, W_OK) == 0)
64 {
65 uid_t uid = geteuid ();
66 /* See if we can become root */
67 if (uid && getuid () == 0 && seteuid (0) == 0)
68 {
69 *puid = uid;
70 return 0;
71 }
72 }
73 }
74 return ec;
75 }
76
77 int
mu_daemon_create_pidfile(const char * filename)78 mu_daemon_create_pidfile (const char *filename)
79 {
80 char *p;
81 int fd;
82 uid_t uid = 0;
83 int rc;
84
85 if (filename[0] != '/')
86 return EINVAL;
87
88 if (pidfile)
89 free (pidfile);
90 pidfile = strdup (filename);
91 if (!pidfile)
92 return ENOMEM;
93
94 /* Determine the hosting directory name */
95 p = strrchr (pidfile, '/');
96 if (pidfile == p)
97 {
98 free (pidfile);
99 pidfile = NULL;
100 /* Sorry, pidfiles in root dir are not allowed */
101 return EINVAL;
102 }
103 /* Check if we have write access to the directory */
104 *p = 0;
105 rc = access_dir (pidfile, &uid);
106 if (rc)
107 {
108 /* Nope, clean up and return */
109 free (pidfile);
110 pidfile = NULL;
111 return rc;
112 }
113
114 /* Restore directory separator */
115 *p = '/';
116
117 unlink (pidfile);
118 current_pid = getpid ();
119
120 if ((fd = open (pidfile, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0644)) != -1)
121 {
122 FILE *fp = fdopen (fd, "w");
123 if (!fp)
124 {
125 rc = errno;
126 free (pidfile);
127 close (fd);
128 }
129 else
130 {
131 fprintf (fp, "%lu", (unsigned long) current_pid);
132 fclose (fp);
133 atexit (mu_daemon_remove_pidfile);
134 }
135 }
136 else
137 {
138 rc = errno;
139 free (pidfile);
140 pidfile = NULL;
141 }
142
143 /* Restore previous EUID value. */
144 if (uid)
145 seteuid (uid);
146
147 return rc;
148 }
149
150 void
mu_daemon_remove_pidfile(void)151 mu_daemon_remove_pidfile (void)
152 {
153 if (getpid () == current_pid)
154 {
155 int rc;
156 uid_t uid = 0;
157
158 /* Determine the hosting directory name */
159 char *p = strrchr (pidfile, '/');
160 if (pidfile == p)
161 {
162 /* Should not happen */
163 abort ();
164 }
165 /* Check if we have write access to the directory */
166 *p = 0;
167 rc = access_dir (pidfile, &uid);
168 *p = '/';
169 if (rc == 0)
170 {
171 if (unlink (pidfile) && errno != ENOENT)
172 rc = errno;
173 else
174 rc = 0;
175 }
176
177 if (rc)
178 mu_error (_("cannot remove pidfile %s: %s"),
179 pidfile, mu_strerror (rc));
180
181 free (pidfile);
182 pidfile = NULL;
183 }
184 }
185
186
187
188