Fix spelling mistakes
[vlendec/samba-autobuild/.git] / ctdb / common / event_script.c
1 /*
2    Low level event script handling
3
4    Copyright (C) Amitay Isaacs  2017
5    Copyright (C) Martin Schwenke  2018
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "replace.h"
22 #include "system/filesys.h"
23 #include "system/dir.h"
24 #include "system/glob.h"
25
26 #include <talloc.h>
27
28 #include "common/event_script.h"
29
30 static int script_filter(const struct dirent *de)
31 {
32         int ret;
33
34         /* Match a script pattern */
35         ret = fnmatch("[0-9][0-9].*.script", de->d_name, 0);
36         if (ret == 0) {
37                 return 1;
38         }
39
40         return 0;
41 }
42
43 int event_script_get_list(TALLOC_CTX *mem_ctx,
44                           const char *script_dir,
45                           struct event_script_list **out)
46 {
47         struct dirent **namelist = NULL;
48         struct event_script_list *script_list = NULL;
49         size_t ds_len;
50         int count, ret;
51         int i;
52
53         count = scandir(script_dir, &namelist, script_filter, alphasort);
54         if (count == -1) {
55                 ret = errno;
56                 goto done;
57         }
58
59         script_list = talloc_zero(mem_ctx, struct event_script_list);
60         if (script_list == NULL) {
61                 goto nomem;
62         }
63
64         if (count == 0) {
65                 ret = 0;
66                 *out = script_list;
67                 goto done;
68         }
69
70         script_list->num_scripts = count;
71         script_list->script = talloc_zero_array(script_list,
72                                                 struct event_script *,
73                                                 count);
74         if (script_list->script == NULL) {
75                 goto nomem;
76         }
77
78         ds_len = strlen(".script");
79         for (i = 0; i < count; i++) {
80                 struct event_script *s;
81                 struct stat statbuf;
82
83                 s = talloc_zero(script_list->script, struct event_script);
84                 if (s == NULL) {
85                         goto nomem;
86                 }
87
88                 script_list->script[i] = s;
89
90                 s->name = talloc_strndup(script_list->script,
91                                          namelist[i]->d_name,
92                                          strlen(namelist[i]->d_name) - ds_len);
93                 if (s->name == NULL) {
94                         goto nomem;
95                 }
96
97                 s->path = talloc_asprintf(script_list->script,
98                                           "%s/%s",
99                                           script_dir,
100                                           namelist[i]->d_name);
101                 if (s->path == NULL) {
102                         goto nomem;
103                 }
104
105                 ret = stat(s->path, &statbuf);
106                 if (ret == 0) {
107                         /*
108                          * If ret != 0 this is either a dangling
109                          * symlink or it has just disappeared.  Either
110                          * way, it isn't executable.  See the note
111                          * below about things that have disappeared.
112                          */
113                         if (statbuf.st_mode & S_IXUSR) {
114                                 s->enabled = true;
115                         }
116                 }
117         }
118
119         *out = script_list;
120         return 0;
121
122 nomem:
123         ret = ENOMEM;
124         talloc_free(script_list);
125
126 done:
127         if (namelist != NULL && count != -1) {
128                 for (i=0; i<count; i++) {
129                         free(namelist[i]);
130                 }
131                 free(namelist);
132         }
133
134         return ret;
135 }
136
137 int event_script_chmod(const char *script_dir,
138                        const char *script_name,
139                        bool enable)
140 {
141         const char *dot_script = ".script";
142         size_t ds_len = strlen(dot_script);
143         size_t sn_len = strlen(script_name);
144         DIR *dirp;
145         struct dirent *de;
146         char buf[PATH_MAX];
147         const char *script_file;
148         int ret, new_mode;
149         char filename[PATH_MAX];
150         struct stat st;
151         bool found;
152         ino_t found_inode;
153         int fd = -1;
154
155         /* Allow script_name to already have ".script" suffix */
156         if (sn_len > ds_len &&
157             strcmp(&script_name[sn_len - ds_len], dot_script) == 0) {
158                 script_file = script_name;
159         } else {
160                 ret = snprintf(buf, sizeof(buf), "%s.script", script_name);
161                 if (ret >= sizeof(buf)) {
162                         return ENAMETOOLONG;
163                 }
164                 script_file = buf;
165         }
166
167         dirp = opendir(script_dir);
168         if (dirp == NULL) {
169                 return errno;
170         }
171
172         found = false;
173         while ((de = readdir(dirp)) != NULL) {
174                 if (strcmp(de->d_name, script_file) == 0) {
175                         /* check for valid script names */
176                         ret = script_filter(de);
177                         if (ret == 0) {
178                                 closedir(dirp);
179                                 return EINVAL;
180                         }
181
182                         found = true;
183                         found_inode = de->d_ino;
184                         break;
185                 }
186         }
187         closedir(dirp);
188
189         if (! found) {
190                 return ENOENT;
191         }
192
193         ret = snprintf(filename,
194                        sizeof(filename),
195                        "%s/%s",
196                        script_dir,
197                        script_file);
198         if (ret >= sizeof(filename)) {
199                 return ENAMETOOLONG;
200         }
201
202         fd = open(filename, O_RDWR);
203         if (fd == -1) {
204                 ret = errno;
205                 goto done;
206         }
207
208         ret = fstat(fd, &st);
209         if (ret != 0) {
210                 ret = errno;
211                 goto done;
212         }
213
214         /*
215          * If the directory entry inode number doesn't match the one
216          * returned by fstat() then this is probably a symlink, so the
217          * caller should not be calling this function.  Note that this
218          * is a cheap sanity check to catch most programming errors.
219          * This doesn't cost any extra system calls but can still miss
220          * the unlikely case where the symlink is to a file on a
221          * different filesystem with the same inode number as the
222          * symlink.
223          */
224         if (found && found_inode != st.st_ino) {
225                 ret = EINVAL;
226                 goto done;
227         }
228
229         if (enable) {
230                 new_mode = st.st_mode | (S_IXUSR | S_IXGRP | S_IXOTH);
231         } else {
232                 new_mode = st.st_mode & ~(S_IXUSR | S_IXGRP | S_IXOTH);
233         }
234
235         ret = fchmod(fd, new_mode);
236         if (ret != 0) {
237                 ret = errno;
238                 goto done;
239         }
240
241 done:
242         if (fd != -1) {
243                 close(fd);
244         }
245         return ret;
246 }