summaryrefslogtreecommitdiff
path: root/nslcd/hackers_watch.c
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@sbcglobal.net>2014-11-28 15:17:16 -0500
committerLuke Shumaker <lukeshu@sbcglobal.net>2014-11-28 15:17:16 -0500
commitd8c48a5aae8f3561cec14f7f08c35fc3619ef00a (patch)
treec0c11cefff91cb8d34deb1091d4d02a1bb71f3d0 /nslcd/hackers_watch.c
parentc9618dfe442305531ee6cab9660333f4a697e094 (diff)
foo
Diffstat (limited to 'nslcd/hackers_watch.c')
-rw-r--r--nslcd/hackers_watch.c188
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;
+}