package hackers_git import ( "inotify" "inotify/inutil" "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.Debug("hackers.git: %v", err) } o.load_user_password(uid) } func (o *Hackers) unwatchHomedir(wd inotify.Wd) { err := o.in_fd.RmWatch(wd) if err != nil { logger.Warning("hackers.git: %v", err) return } uid := o.in_wd2uid[wd] delete(o.in_wd2uid, wd) delete(o.in_uid2wd, uid) o.load_user_password(uid) } func (o *Hackers) close() { if o.in_fd != nil { o.in_fd.Close() defer o.workers.Wait() } o.in_wd_home = -1 o.in_wd_yaml = -1 o.users = make(map[int32]user, 0) o.groups = make(map[string]map[string]bool) o.in_uid2wd = make(map[int32]inotify.Wd, 0) o.in_wd2uid = make(map[inotify.Wd]int32, 0) } func (o *Hackers) reload() (err error) { o.close() o.in_fd, err = inutil.WatcherInit() ; 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.groups = make(map[string]map[string]bool) o.in_uid2wd = make(map[int32]inotify.Wd, len(filenames)) o.in_wd2uid = make(map[inotify.Wd]int32, len(filenames)) for _, filename := range filenames { o.load_yaml_file(filename, false) } go func() { defer lsb.Recover() defer o.workers.Done() o.worker() }() err = nil return } func (o *Hackers) add_user_to_group(username string, groupname string) { group, found := o.groups[groupname] if !found { group = make(map[string]bool) o.groups[groupname] = group } group[username] = true } func (o *Hackers) del_user_from_group(username string, groupname string) { group, found := o.groups[groupname] if !found { return } delete(group, username) if len(group) < 1 { delete(o.groups, groupname) } } func (o *Hackers) load_yaml_file(filename string, uselock bool) { logger.Debug("hackers.git: Loading YAML file: %s", filename) user, err := parse_user_yaml(filename) uid := user.passwd.UID if err == nil { logger.Debug("hackers.git: -> User %d added/updated", uid) if uselock { o.lock.Lock() defer o.lock.Unlock() } if olduser, found := o.users[uid]; found { for _, groupname := range olduser.groups { o.del_user_from_group(olduser.passwd.Name, groupname) } } for _, groupname := range user.groups { o.add_user_to_group(user.passwd.Name, groupname) } o.users[uid] = user o.watchHomedir(uid) } else if uid >= 0 { // User became invalid logger.Debug("hackers.git: -> User %d invalidated: %v", uid, err) if uselock { o.lock.Lock() defer o.lock.Unlock() } if wd, found := o.in_uid2wd[uid]; found { o.unwatchHomedir(wd) } if olduser, found := o.users[uid]; found { for _, groupname := range olduser.groups { o.del_user_from_group(olduser.passwd.Name, groupname) } delete(o.users, uid) } } else { logger.Debug("hackers.git: -> File ignored: %v", err) } } func (o *Hackers) load_user_password(uid int32) { user := o.users[uid] user.passwd.PwHash = parse_user_password(user.passwd.HomeDir + "/.password") o.users[uid] = user } func (o *Hackers) worker_watch_homedirs() { for uid, _ := range o.users { o.watchHomedir(uid) } } func worker_error(format string, a ...interface{}) { logger.Err("hackers.git: "+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) } Loop: for { select { case event, ok := <-o.in_fd.Events: if !ok { break Loop } 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 { if event.Name == nil { panic("recieved child event from inotify, but no child name") } o.load_yaml_file(o.Cfg.Yamldir+"/"+*event.Name, true) } 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 != nil { if *event.Name == ".password" { func() { o.lock.Lock() defer o.lock.Unlock() o.load_user_password(o.in_wd2uid[event.Wd]) }() } } else { logger.Debug("hackers.git: Event didn't match: %#v", event) } } case err, ok := <-o.in_fd.Errors: if !ok { break Loop } logger.Warning("hackers.git: Inotify error: %v", err) } } logger.Info("hackers.git: Stopped inotify watcher") }