diff options
| author | Luke Shumaker <lukeshu@sbcglobal.net> | 2014-11-28 15:17:16 -0500 |
|---|---|---|
| committer | Luke Shumaker <lukeshu@sbcglobal.net> | 2014-11-28 15:17:16 -0500 |
| commit | d8c48a5aae8f3561cec14f7f08c35fc3619ef00a (patch) | |
| tree | c0c11cefff91cb8d34deb1091d4d02a1bb71f3d0 /nslcd/hackers_watch.c | |
| parent | c9618dfe442305531ee6cab9660333f4a697e094 (diff) | |
foo
Diffstat (limited to 'nslcd/hackers_watch.c')
| -rw-r--r-- | nslcd/hackers_watch.c | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/nslcd/hackers_watch.c b/nslcd/hackers_watch.c new file mode 100644 index 0000000..5217163 --- /dev/null +++ b/nslcd/hackers_watch.c @@ -0,0 +1,188 @@ +/* hackers_watch.c - watch for changes in hackers.git + * + * Copyright (C) 2014 Luke Shumaker + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#define _GNU_SOURCE + +#include <glob.h> +#include <stdio.h> /* for asprintf(3) */ +#include <unistd.h> /* for chdir(3) */ + +#include "inotify_iterator.h" +#include "hackers_parse.h" +#include "hackers_watch.h" + +#define EVENT_FILE_IS(event, str) \ + (((event)->len == sizeof(str)) && (strcmp((event)->name, str) == 0)) +#define EVENT_CHILD_ADD (IN_CREATE | IN_MOVED_TO) +#define EVENT_CHILD_DEL (IN_DELETE | IN_MOVED_FROM) +#define EVENT_CHILD_MOD (IN_CLOSE_WRITE | IN_MOVED_TO) +#define EVENT_CHILD_ANY (EVENT_CHILD_ADD | EVENT_CHILD_DEL | EVENT_CHILD_MOD) + +#define WATCH_HOMEDIR(session, i) \ + session->in_user_wds[i] = \ + inotify_add_watch(session->in_fd, \ + session->users[i].pw_dir, \ + EVENT_CHILD_ANY | IN_MOVE_SELF) + +int +hackers_init(const char *yamldir, struct session *sess) { + char *glob_pattern; + glob_t glob_results; + char *filename; + + sess->yamldir = strdup(yamldir); + pthread_rwlock_init(&(sess->lock), NULL); + sess->in_fd = inotify_init(); + sess->in_wd_yaml = inotify_add_watch(sess->in_fd, yamldir, EVENT_CHILD_ANY); + sess->in_wd_home = inotify_add_watch(sess->in_fd, "/home" , EVENT_CHILD_ADD); + + asprintf(&glob_pattern, "%s/*.yml", yamldir); + glob(glob_pattern, 0, NULL, &glob_results); + free(glob_pattern); + + sess->cnt = glob_results.gl_pathc - glob_results.gl_offs; + sess->users = calloc(sess->cnt, sizeof(struct passwd)); + sess->in_user_wds = calloc(sess->cnt, sizeof(int)); + + for (size_t i = 0; i < sess->cnt; i++) { + filename = glob_results.gl_pathv[glob_results.gl_offs+i]; + if (load_user_yaml(filename, &(sess->users[i]))==0) { + WATCH_HOMEDIR(sess, i); + } else { + sess->users[i].pw_uid = 0; + sess->in_user_wds[i] = -1; + } + } + + globfree(&glob_results); + + return 0; +} + +static +void +worker_watch_homedirs(struct session *sess) { + pthread_rwlock_wrlock(&(sess->lock)); + for (size_t i = 0; i < sess->cnt; i++) { + if (sess->users[i].pw_uid > 0) { + int oldwd = sess->in_user_wds[i]; + WATCH_HOMEDIR(sess, i); + if (oldwd != sess->in_user_wds[i]) { + if (oldwd != -1) + inotify_rm_watch(sess->in_fd, oldwd); + load_user_password(&(sess->users[i])); + } + } + } + pthread_rwlock_unlock(&(sess->lock)); +} + +static +void +worker_handle_add_yaml(struct session *sess, struct passwd *newdata) { + /* We have to first see if this is for an existing UID, then + * if it's not, we need to find an empty slot, potentially + * realloc()ing the array. + */ + pthread_rwlock_wrlock(&(sess->lock)); + ssize_t spot = -1; + for (size_t i = 0; i < sess->cnt; i++) { + if (spot < 0 && sess->users[i].pw_uid == 0) { + spot = i; + } + if (sess->users[i].pw_uid == newdata->pw_uid) { + spot = i; + break; + } + } + if (spot < 0) { + /* must grow the array */ + spot = sess->cnt++; + sess->users = realloc(sess->users , sess->cnt * sizeof(struct passwd)); + sess->in_user_wds = realloc(sess->in_user_wds, sess->cnt * sizeof(int)); + ZERO(sess->users[spot]); + } else if (sess->users[spot].pw_uid != 0) { + PASSWD_FREE(sess->users[spot]); + } + sess->users[spot] = *newdata; + pthread_rwlock_unlock(&(sess->lock)); +} + +static +void +worker_handle_del_yaml(struct session *sess, uid_t uid) { + pthread_rwlock_wrlock(&(sess->lock)); + for (size_t i = 0; i < sess->cnt; i++) { + if (sess->users[i].pw_uid == uid) { + PASSWD_FREE(sess->users[i]); + inotify_rm_watch(sess->in_fd, sess->in_user_wds[i]); + break; + } + } + pthread_rwlock_unlock(&(sess->lock)); +} + +int +hackers_worker(struct session *sess) { + chdir(sess->yamldir); + struct inotify_event *event; + for (INOTIFY_ITERATOR(sess->in_fd, event)) { + if (event->wd == sess->in_wd_yaml) { + /* handle updates to yaml files */ + if (event->mask & (EVENT_CHILD_ADD | EVENT_CHILD_MOD)) { + struct passwd user; ZERO(user); + if (load_user_yaml(event->name, &user)==0) { + /* User added/updated */ + worker_handle_add_yaml(sess, &user); + } else if (user.pw_uid > 0) { + /* User became invalid */ + worker_handle_del_yaml(sess, + user.pw_uid); + } + } else if (event->mask & EVENT_CHILD_DEL) { + uid_t uid = filename2uid(event->name); + if (uid > 0) { + worker_handle_del_yaml(sess, uid); + } + } + } else if ((event->wd == sess->in_wd_home) + && (event->mask & IN_ISDIR)) { + /* handle added home directory */ + worker_watch_homedirs(sess); + } else { + /* handle a change to someone's password */ + if (event->mask & IN_MOVE_SELF) { + inotify_rm_watch(sess->in_fd, event->wd); + worker_watch_homedirs(sess); + } + if ((event->mask & IN_IGNORED) + || EVENT_FILE_IS(event, ".password")) { + pthread_rwlock_wrlock(&(sess->lock)); + for (size_t i = 0; i < sess->cnt; i++) { + if (sess->in_user_wds[i] == event->wd) { + if (event->mask & IN_IGNORED) + sess->in_user_wds[i] = -1; + load_user_password(&(sess->users[i])); + break; + } + } + pthread_rwlock_unlock(&(sess->lock)); + } + } + } + return -1; +} |
