// Allocate a new Murmur instance func NewServer(id int64) (s *Server, err error) { s = new(Server) s.Id = id s.cfg = serverconf.New(nil) s.Users = make(map[uint32]*User) s.UserCertMap = make(map[string]*User) s.UserNameMap = make(map[string]*User) s.Users[0], err = NewUser(0, "SuperUser") s.UserNameMap["SuperUser"] = s.Users[0] s.nextUserId = 1 s.Channels = make(map[int]*Channel) s.Channels[0] = NewChannel(0, "Root") s.nextChanId = 1 s.Logger = log.New(&logtarget.Target, fmt.Sprintf("[%v] ", s.Id), log.LstdFlags|log.Lmicroseconds) return }
// Create a new server from its on-disk representation. // // This will read a full serialized server (typically stored in // a file called 'main.fz') from disk. It will also check for // a log file ('log.fz') and iterate through the entries of the log // file and apply the updates incrementally to the server. // // Once both the full server and the log file has been merged together // in memory, a new full seralized server will be written and synced to // disk, and the existing log file will be removed. func NewServerFromFrozen(name string) (s *Server, err error) { id, err := strconv.ParseInt(name, 10, 64) if err != nil { return nil, err } path := filepath.Join(Args.DataDir, "servers", name) mainFile := filepath.Join(path, "main.fz") backupFile := filepath.Join(path, "backup.fz") logFn := filepath.Join(path, "log.fz") r, err := os.Open(mainFile) if os.IsNotExist(err) { err = os.Rename(backupFile, mainFile) if err != nil { return nil, err } r, err = os.Open(mainFile) if err != nil { return nil, err } } else if err != nil { return nil, err } defer r.Close() buf, err := ioutil.ReadAll(r) if err != nil { return nil, err } // Unmarshal the server from it's frozen state fs := freezer.Server{} err = proto.Unmarshal(buf, &fs) if err != nil { return nil, err } // Create a config map from the frozen server. cfgMap := map[string]string{} for _, cfgEntry := range fs.Config { if cfgEntry.Key != nil && cfgEntry.Value != nil { cfgMap[*cfgEntry.Key] = *cfgEntry.Value } } s, err = NewServer(id) if err != nil { return nil, err } s.cfg = serverconf.New(cfgMap) // Unfreeze the server's frozen bans. s.UnfreezeBanList(fs.BanList) // Add all channels, but don't hook up parent/child relationships // until after we've walked the log file. No need to make it harder // than it really is. parents := make(map[uint32]uint32) for _, fc := range fs.Channels { // The frozen channel must contain an Id and a Name, // since the server's frozen channels are guaranteed to // not be deltas. if fc.Id == nil || fc.Name == nil { continue } // Create the channel on the server. // Update the server's nextChanId field if it needs to be, // to make sure the server doesn't re-use channel id's. c := NewChannel(int(*fc.Id), *fc.Name) if c.Id >= s.nextChanId { s.nextChanId = c.Id + 1 } // Update the channel with the contents of the freezer.Channel. c.Unfreeze(fc) // Add the channel's id to the server's channel-id-map. s.Channels[c.Id] = c // Mark the channel's parent if fc.ParentId != nil { parents[*fc.Id] = *fc.ParentId } else { delete(parents, *fc.Id) } } // Add all users for _, fu := range fs.Users { if fu.Id == nil && fu.Name == nil { continue } u, err := NewUser(*fu.Id, *fu.Name) if err != nil { return nil, err } if u.Id >= s.nextUserId { s.nextUserId = u.Id + 1 } // Merge the contents of the freezer.User into // the user struct. u.Unfreeze(fu) // Update the server's user maps to point correctly // to the new user. s.Users[u.Id] = u s.UserNameMap[u.Name] = u if len(u.CertHash) > 0 { s.UserCertMap[u.CertHash] = u } } // Attempt to walk the stored log file logFile, err := os.Open(logFn) walker, err := freezer.NewReaderWalker(logFile) if err != nil { return nil, err } for { values, err := walker.Next() if err == io.EOF { err = logFile.Close() if err != nil { return nil, err } break } else if err != nil { return nil, err } for _, val := range values { switch val.(type) { case *freezer.User: fu := val.(*freezer.User) // Check if it's a valid freezer.User message. It must at least // have the Id field filled out for us to be able to do anything // with it. Warn the admin if an illegal entry is encountered. if fu.Id == nil { log.Printf("Skipped User log entry: No id given.") continue } userId := *fu.Id // Determine whether the user already exists on the server or not. // If the user already exists, this log entry simply updates the // data for that user. // If the user doesn't exist, we create it with the data given in // this log entry. user, ok := s.Users[userId] if !ok { // If no name is given in the log entry, skip this entry. // Also, warn the admin. if fu.Name == nil { log.Printf("Skipped User creation log entry: No name given.") continue } // Create the new user and increment the UserId // counter for the server if needed. user, err = NewUser(userId, *fu.Name) if err != nil { return nil, err } if user.Id >= s.nextUserId { s.nextUserId = user.Id + 1 } } // Merge the contents of the frozen.User into the // user struct. user.Unfreeze(fu) // Update the various user maps in the server to // be able to correctly look up the user. s.Users[user.Id] = user s.UserNameMap[user.Name] = user if len(user.CertHash) > 0 { s.UserCertMap[user.CertHash] = user } case *freezer.UserRemove: fu := val.(*freezer.UserRemove) // Check for an invalid message and warn if appropriate. if fu.Id == nil { log.Printf("Skipped UserRemove log entry: No id given.") continue } userId := *fu.Id // Does this user even exist? // Warn if we encounter an illegal delete op. user, ok := s.Users[userId] if ok { // Clear the server maps. That should do it. delete(s.Users, userId) delete(s.UserNameMap, user.Name) if len(user.CertHash) > 0 { delete(s.UserCertMap, user.CertHash) } } else { log.Printf("Skipped UserRemove log entry: No user for given id.") continue } case *freezer.Channel: fc := val.(*freezer.Channel) // Check whether the log entry is legal. if fc.Id == nil { log.Printf("Skipped Channel log entry: No id given.") continue } channelId := int(*fc.Id) channel, alreadyExists := s.Channels[channelId] if !alreadyExists { if fc.Name == nil { log.Printf("Skipped Channel creation log entry: No name given.") continue } // Add the channel and increment the server's // nextChanId field to a consistent state. channel = NewChannel(channelId, *fc.Name) if channel.Id >= s.nextChanId { s.nextChanId = channel.Id + 1 } } // Unfreeze the contents of the frozen channel // into the existing or newly-created channel. channel.Unfreeze(fc) // Re-add it to the server's channel map (in case // the channel was newly-created) s.Channels[channelId] = channel // Mark the channel's parent if !alreadyExists { if fc.ParentId != nil { parents[*fc.Id] = *fc.ParentId } else { delete(parents, *fc.Id) } } case *freezer.ChannelRemove: fc := val.(*freezer.ChannelRemove) if fc.Id == nil { log.Printf("Skipped ChannelRemove log entry: No id given.") continue } s.Channels[int(*fc.Id)] = nil delete(parents, *fc.Id) case *freezer.BanList: fbl := val.(*freezer.BanList) s.UnfreezeBanList(fbl) case *freezer.ConfigKeyValuePair: fcfg := val.(*freezer.ConfigKeyValuePair) if fcfg.Key != nil { // It's an update operation if fcfg.Value != nil { s.cfg.Set(*fcfg.Key, *fcfg.Value) // It's a delete/reset operation. } else { s.cfg.Reset(*fcfg.Key) } } } } } // Hook up children with their parents for chanId, parentId := range parents { childChan, exists := s.Channels[int(chanId)] if !exists { return nil, errors.New("Non-existant child channel") } parentChan, exists := s.Channels[int(parentId)] if !exists { return nil, errors.New("Non-existant parent channel") } parentChan.AddChild(childChan) } // Hook up all channel links for _, channel := range s.Channels { if len(channel.Links) > 0 { links := channel.Links channel.Links = make(map[int]*Channel) for chanId, _ := range links { targetChannel := s.Channels[chanId] if targetChannel != nil { s.LinkChannels(channel, targetChannel) } } } } return s, nil }