package hackers_git import ( "inotify" "os" "path/filepath" "sd_daemon/logger" "sd_daemon/lsb" ) const ( in_CHILD_ADD = inotify.IN_CREATE | inotify.IN_MOVED_TO in_CHILD_DEL = inotify.IN_DELETE | inotify.IN_MOVED_FROM in_CHILD_MOD = inotify.IN_CLOSE_WRITE | inotify.IN_MOVED_TO in_CHILD_ANY = in_CHILD_ADD | in_CHILD_DEL | in_CHILD_MOD in_DIR_INVALID = inotify.IN_MOVE_SELF | inotify.IN_DELETE_SELF in_DIR = inotify.IN_ONLYDIR | in_DIR_INVALID ) func (o *Hackers) watchHomedir(uid int32) { wd, err := o.in_fd.AddWatch(o.users[uid].passwd.HomeDir, in_DIR|in_CHILD_ANY) if err == nil { oldwd, found := o.in_uid2wd[uid] if found && oldwd != wd { o.unwatchHomedir(oldwd) } o.in_uid2wd[uid] = wd o.in_wd2uid[wd] = uid } else { delete(o.in_uid2wd, uid) logger.Info("could not watch: %s", o.users[uid].passwd.HomeDir) } } func (o *Hackers) unwatchHomedir(wd inotify.Cint) { err := o.in_fd.RmWatch(wd) if err != nil { logger.Warning("could not remove watch: %v", wd) return } uid := o.in_wd2uid[wd] delete(o.in_wd2uid, wd) delete(o.in_uid2wd, uid) } func NewHackers(config Config) *Hackers { o := Hackers{ cfg: config, } err := o.reload() if err != nil { return nil } go o.worker() return &o } func (o *Hackers) close() { if o.in_fd != nil { o.in_fd.Close() o.in_fd = nil } o.in_wd_home = -1 o.in_wd_yaml = -1 o.users = make(map[int32]user, 0) o.in_uid2wd = make(map[int32]inotify.Cint, 0) o.in_wd2uid = make(map[inotify.Cint]int32, 0) } func (o *Hackers) reload() (err error) { o.close() o.in_fd, err = inotify.InotifyInit() ; if err != nil { return } o.in_wd_home, err = o.in_fd.AddWatch("/home" , in_DIR|in_CHILD_ADD); if err != nil { return } o.in_wd_yaml, err = o.in_fd.AddWatch(o.cfg.Yamldir, in_DIR|in_CHILD_ANY); if err != nil { return } filenames, err := filepath.Glob(o.cfg.Yamldir + "/*.yml") o.users = make(map[int32]user, len(filenames)) o.in_uid2wd = make(map[int32]inotify.Cint, len(filenames)) o.in_wd2uid = make(map[inotify.Cint]int32, len(filenames)) for _, filename := range filenames { logger.Info("Loading yaml file: %s", filename) user, err := load_user_yaml(filename) if err == nil { o.users[user.passwd.UID] = user logger.Info("... success") o.watchHomedir(user.passwd.UID) o.passwords[user.passwd.UID] = load_user_password(user.passwd.HomeDir + "/.password") } else { logger.Info("... error: %v", err) } } err = nil return } func (o *Hackers) worker_handle_user_add(user user) { o.lock.Lock() defer o.lock.Unlock() o.users[user.passwd.UID] = user o.watchHomedir(user.passwd.UID) o.passwords[user.passwd.UID] = load_user_password(user.passwd.HomeDir + "/.password") } func (o *Hackers) worker_handle_user_del(uid int32) { o.lock.Lock() defer o.lock.Unlock() wd, found := o.in_uid2wd[uid] if found { o.unwatchHomedir(wd) } delete(o.users, uid) } func (o *Hackers) worker_watch_homedirs() { for uid, _ := range o.users { o.watchHomedir(uid) } } func (o *Hackers) worker_handle_passwd(uid int32) { o.lock.Lock() defer o.lock.Unlock() o.passwords[uid] = load_user_password(o.users[uid].passwd.HomeDir + "/.password") } func worker_error(format string, a ...interface{}) { logger.Err(format, a) os.Exit(int(lsb.EXIT_FAILURE)) } func (o *Hackers) worker() { err := os.Chdir(o.cfg.Yamldir) if err != nil { worker_error("failed to load %q: %v", o.cfg.Yamldir, err) } for event, err := o.in_fd.Read(); err == nil; event, err = o.in_fd.Read() { switch event.Wd { case o.in_wd_yaml: // handle updates to yaml files if event.Mask&in_DIR_INVALID != 0 { err := o.Reload() if err != nil { worker_error("failed to reload hackers.git yaml directory: %v", err) } err = os.Chdir(o.cfg.Yamldir) if err != nil { worker_error("failed to load %q: %v", o.cfg.Yamldir, err) } } else if event.Mask&in_CHILD_ANY != 0 { filename := o.cfg.Yamldir + "/" + *event.Name logger.Info("Loading yaml file: %s", filename) user, err := load_user_yaml(filename) if err == nil { // User added/updated logger.Info("... success") o.worker_handle_user_add(user) } else if user.passwd.UID >= 0 { // User became invalid logger.Info("... error: %v", err) o.worker_handle_user_del(user.passwd.UID) } } else { panic("recieved non-subscribed inotify event from kernel") } case o.in_wd_home: if event.Mask&in_DIR_INVALID != 0 { err := o.Reload() if err != nil { panic(err) } } else if event.Mask&inotify.IN_ISDIR != 0 { // handle added home directory o.worker_watch_homedirs() } default: // handle a change to someone's password if event.Mask&in_DIR_INVALID != 0 { o.unwatchHomedir(event.Wd) o.worker_watch_homedirs() } else if *event.Name == ".password" { o.worker_handle_passwd(o.in_wd2uid[event.Wd]) } } } panic("not reached") }