diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2019-09-25 15:08:01 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2019-09-25 15:14:08 +0300 |
commit | 1da3cd08079eaea4f6ec6bb73c2c22c829cb4449 (patch) | |
tree | 55b24aeb2dbfd554f4000fb8ad17f77f0ae9f09c | |
parent | 6856c09b11993f35fef72a32c21b166ae183f5d6 (diff) | |
download | proto-1da3cd08079eaea4f6ec6bb73c2c22c829cb4449.tar.gz proto-1da3cd08079eaea4f6ec6bb73c2c22c829cb4449.tar.bz2 |
Document functions in main.go
* depmap.go: Document some functions
* main.go (ListingRow): Use immediate allocation for the on
member.
(LoadListing,Set): Update accordingly.
(TraverseMatchingRows): Rename to TraverseEquidependentServices.
(DetectRecursion): Simplify. The function needs a rewrite.
Document all functions
-rw-r--r-- | depmap.go | 28 | ||||
-rwxr-xr-x | etc/init.d/D | 2 | ||||
-rwxr-xr-x | etc/init.d/E | 2 | ||||
-rwxr-xr-x | etc/init.d/F | 9 | ||||
-rw-r--r-- | main.go | 146 |
5 files changed, 118 insertions, 69 deletions
@@ -51,18 +51,6 @@ func (dm *Depmap) IsSet(i int, j int) bool { return dm.mtx[i][j] } -func (dm *Depmap) FindCycles() ([]int, bool) { - tc := dm.Copy() - tc.TC() - var ret []int - for i := 0; i < dm.dim; i++ { - if dm.mtx[i][i] { - ret = append(ret, i) - } - } - return ret, len(ret) > 0 -} - type DepmapIterator func () (int, bool) func (dm *Depmap) TraverseRow(i int) (DepmapIterator, error) { @@ -74,7 +62,7 @@ func (dm *Depmap) TraverseRow(i int) (DepmapIterator, error) { for j < dm.dim { k := j j++ - if (dm.mtx[i][k]) { + if dm.IsSet(i, k) { return k, true } } @@ -99,6 +87,8 @@ func (dm *Depmap) TraverseColumn(i int) (DepmapIterator, error) { }, nil } +// Return an iterator over each row that matches exactly row n, i.e. +// reflects the same dependencies. func (dm *Depmap) TraverseMatchingRows (n int) (DepmapIterator, error) { if n < 0 || n > dm.dim { return nil, errors.New(strconv.Itoa(n) + ": out of bounds"); @@ -125,3 +115,15 @@ func (dm *Depmap) TraverseMatchingRows (n int) (DepmapIterator, error) { return -1, false }, nil } + +func (dm *Depmap) FindCycles() ([]int) { + tc := dm.Copy() + tc.TC() + var ret []int + for i := 0; i < tc.dim; i++ { + if tc.IsSet(i, i) { + ret = append(ret, i) + } + } + return ret +} diff --git a/etc/init.d/D b/etc/init.d/D index 0ce4e0c..b0941bd 100755 --- a/etc/init.d/D +++ b/etc/init.d/D @@ -6,7 +6,7 @@ # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Fake service D -# Description: Fake service C depends on both A and C on startup, +# Description: Fake service D depends on both A and C on startup, # but only on A on shutdown. # It should get the sequence number greater than C for start, and the # same number as B on shutdown. diff --git a/etc/init.d/E b/etc/init.d/E index f607335..0cec2ea 100755 --- a/etc/init.d/E +++ b/etc/init.d/E @@ -8,7 +8,7 @@ # Short-Description: Fake service E # Description: Fake service E depends on both A and C. # In levels 3 4 5 it should get the sequence number greater than C. -# In levels 0 1 6 it should get the number less than C. +# In levels 0 1 6 it should get the number less than A. # In level 2 it should get the highest priority - 1 (not optimal perhaps) ### END INIT INFO exit 0 diff --git a/etc/init.d/F b/etc/init.d/F index 80c9346..87f7471 100755 --- a/etc/init.d/F +++ b/etc/init.d/F @@ -5,10 +5,9 @@ # Required-Stop: # Default-Start: 3 4 5 # Default-Stop: 0 1 2 6 -# Short-Description: Fake service E -# Description: Fake service E depends on both A and C. -# In levels 3 4 5 it should get the sequence number greater than C. -# In levels 0 1 6 it should get the number less than C. -# In level 2 it should get the highest priority - 1 (not optimal perhaps) +# Short-Description: Fake service F +# Description: Fake service F depends on built-in $named +# In levels 3 4 5 it should get the sequence number 98 +# In levels 0 1 6 it should get the sequence number 1 ### END INIT INFO exit 0 @@ -92,7 +92,7 @@ func (a *ArgList) Len() int { } func (a *ArgList) Consumed() bool { - return len(a.args) == 0 + return a.Len() == 0 } func (a *ArgList) Report() { @@ -122,11 +122,14 @@ func GetRunlevels() Runlevels { // 1. Get service listing // ------------------------------------- +// A row in the listing type ListingRow struct { - name string - on []bool + name string // Service base name + on [NumLevels]bool // Indicators of whether the service is enabled + // on each runlevel } +// The listing structure type Listing struct { rows []ListingRow } @@ -134,56 +137,69 @@ type Listing struct { type NameSelector func (string) bool type NameChecker func () bool +// Create a Listing object for services from initdir that satisfy sel. +// Returns a pointer to the created object and error status. func LoadListing(initdir *InitDir, sel NameSelector) (*Listing, error) { var lst Listing next := initdir.Iterator(sel) srv := next() for srv != nil { - lst.rows = append(lst.rows, ListingRow{name: srv.BaseName(), on: make([]bool, NumLevels)}) + lst.rows = append(lst.rows, ListingRow{name: srv.BaseName()}) srv = next() } return &lst, nil } +// Mark the service name in the listing lst as enabled for runlevel lev. func (lst *Listing) Set(name string, lev Runlevel) { - for _, row := range lst.rows { - if row.name == name { - row.on[lev.Index()] = true + for i := range lst.rows { + if lst.rows[i].name == name { + lst.rows[i].on[lev.Index()] = true return } } } +// slackserv --list command func List(args *ArgList) { + // Load the content of the init.d directory. initdir, err := LoadInitDir(InitDirectory) if err != nil { Logger.Fatal(err) } - + + // Runlevels required in command line (by the --level option, if any). rs := GetRunlevels() + + // Create a blank listing structure. lst, err := LoadListing(initdir, args.GetNameSelector()) if err != nil { Logger.Fatal(err) } + // Check and report unconsumed arguments args.Report() + // Iterate over selected runlevels next := rs.Iterator() r := next() for r.IsValid() { + // Scan the rc<r>,d directory. rcd, err := LoadRCDir(r, initdir) if err != nil { continue } + // Iterate over all SystemV services in its start sequence. nextserv := rcd.Start.SysVServiceIterator() srv := nextserv() for srv != nil { + // Mark each such service as enabled for the runlevel. lst.Set(srv.BaseName(), r) srv = nextserv() } - r = next() } + // Print the resulting listing. for _, row := range lst.rows { fmt.Printf("%-16s", row.name) next = rs.Iterator() @@ -201,6 +217,7 @@ func List(args *ArgList) { fmt.Println() } } + // ------------------------------------- // 2. Delete services // ------------------------------------- @@ -210,34 +227,46 @@ func Del(args *ArgList) { Logger.Fatal(`not enough arguments`) } + // Load the content of the init.d directory. initdir, err := LoadInitDir(InitDirectory) if err != nil { Logger.Fatal(err) } + // Get the required runlevels rs := GetRunlevels() + // Collect the names of files to remove var files []string + // Repeat for each service named in the command line: nextservice := initdir.Iterator(args.GetNameSelector()) wanted := nextservice() for wanted != nil { + // iterate over all requested runlevels nextlevel := rs.Iterator() r := nextlevel() for r.IsValid() { + // Scan the rc<r>,d directory. rcd, err := LoadRCDir(r, initdir) if err != nil { continue } + // Iterate over all SystemV services in its start + // sequence: nextsysv := rcd.Start.SysVServiceIterator() srv := nextsysv() for srv != nil { + // If the service basename matches, add its + // file name to the list. if srv.BaseName() == wanted.BaseName() { files = append(files, srv.FileName()) } srv = nextsysv() } - + + // Do the same for all services in the stop + // sequence. nextsysv = rcd.Stop.SysVServiceIterator() srv = nextsysv() for srv != nil { @@ -252,8 +281,10 @@ func Del(args *ArgList) { wanted = nextservice() } + // Check and report any unconsumed arguments args.Report() - + + // Actually remove the collected files (unless in dry-run mode). for _, file := range files { if opts.Debug { Logger.Printf("DEBUG: removing %s\n", file) @@ -267,16 +298,20 @@ func Del(args *ArgList) { } } } + // ------------------------------------- // 3. Add services // ------------------------------------- +// The Symlink structure contains a single instruction for creating a +// symbolic link to the service file in init.d. type Symlink struct { - dir string - src string - dst string + dir string // Full directory name of the rcN.d directory + src string // Source argument name + dst string // Name of the symlink to create } +// Create a symlink requested by s. func (s Symlink) Create() error { // Find longest common directory prefix if opts.Debug { @@ -316,6 +351,8 @@ func (s Symlink) Create() error { if opts.Debug { Logger.Printf("DEBUG: dir=%s src=%s dst=%s\n", s.dir, s.src, s.dst) } + + // Create the link, unless in dry-run mode: if ! opts.DryRun { cwd, err := os.Getwd() if err != nil { @@ -343,11 +380,13 @@ func Add(args *ArgList) { Logger.Fatal(`not enough arguments`) } + // Load the content of the init.d directory. initdir, err := LoadInitDir(InitDirectory) if err != nil { Logger.Fatal(err) } + // Get the required runlevels rsmask := GetRunlevels() var services []Service // Services to be added @@ -363,17 +402,19 @@ func Add(args *ArgList) { services = append(services, srv) srv = nextservice() } + // If any unused arguments remain, report them and exit. args.Report() - + + // Mask out non requested runlevels rsStart.And(rsmask) rsStop.And(rsmask) // Load rc directories - var rcdir [NumLevels]*RCDir - var rs Runlevels + var rcdir [NumLevels]*RCDir // All needed rcN.d directories + var rs Runlevels // A union of start and stop runlevels rs.Or(rsStart) rs.Or(rsStop) - + // Iterate over all runlevels and load the corresponding rc directories nextlevel := rs.Iterator() r := nextlevel() for r.IsValid() { @@ -385,6 +426,7 @@ func Add(args *ArgList) { r = nextlevel() } + // Create the array of symlinks to be created var symlink []*Symlink // Incorporate each new service into the corresponding runlevels @@ -410,6 +452,7 @@ func Add(args *ArgList) { } } + // Create the symlinks for _, lnk := range symlink { err := lnk.Create() if err != nil { @@ -440,6 +483,8 @@ func NewPriority(isstart bool) Priority { } } +// Start priority implementation +// ----------------------------- func (p *StartPriority) Incr() { p.val++ if p.val > 99 { @@ -454,6 +499,8 @@ func (p *StartPriority) Update(n int) { } } +// Stop priority implementation +// ----------------------------- func (p *StopPriority) Incr() { p.val-- if p.val < 0 { @@ -484,7 +531,8 @@ func (p *StopPriority) String() string { return fmt.Sprintf("%02d", p.Value()) } -// +// Return an iterator over all dependencies of the service n in the +// start sequence. func (rseq *RCSeq) StartTraversal(n int) ServiceIterator { next, err := rseq.Deps.TraverseRow(n) if err != nil { @@ -499,7 +547,9 @@ func (rseq *RCSeq) StartTraversal(n int) ServiceIterator { } } -func (rseq *RCSeq) TraverseMatchingRows(n int) ServiceIterator { +// Return an iterator over all services with the same dependencies as +// service n. +func (rseq *RCSeq) TraverseEquidependentServices(n int) ServiceIterator { next, err := rseq.Deps.TraverseMatchingRows(n) if err != nil { Logger.Fatal(err) @@ -513,37 +563,32 @@ func (rseq *RCSeq) TraverseMatchingRows(n int) ServiceIterator { } } +// Detect and report possible cyclic dependencies. func (rseq *RCSeq) DetectRecursion() { - if idx, found := rseq.Deps.FindCycles(); found { + if idx := rseq.Deps.FindCycles(); len(idx) > 0 { for _, n := range idx { - Logger.Printf("%s depends on itself\n", rseq.Service[n].BaseName()) - next, err := rseq.Deps.TraverseRow(n) - if err != nil { - Logger.Fatal(err) - } - - i, more := next() - for more { - Logger.Printf(" -> %s\n", rseq.Service[i].BaseName()) - i, more = next() - } - os.Exit(1) + Logger.Printf("%s: cyclic dependency\n", + rseq.Service[n].BaseName()) } + os.Exit(1) } } - -func (rseq *RCSeq) Inc(newsrv Service) *Symlink { - // If rseq is a start sequence: - // 1. Set prio = 0 - // 2. Traverse the row i of the dependency matrix. For each element (i,j): - // 2.1. If the element is true. set prio = max(prio, srv(j).prio) - // If rseq is a stop sequence - // 1. Set prio = 99 - // 2. Traverse the column j of the dependency matrix. For each element (i,j): - // 2.1. If the element is true, set prio = min(prio, srv(i).prio - // In both cases - // 3. Recompute the dependency matrix. Should implement incremental TC algorithm for that +// Incorporate service newsrv into sequence rseq +func (rseq *RCSeq) Inc(newsrv Service) *Symlink { + /* 1. Register newserv in rsec. Let N be its number. + 2. Compute dependency matrix + 3. Compute priority for the new service: + 3.1. Set prio to initial value (0 for start sequence, 99 for + stop sequence) + 3.2. Traverse the Nth row of the matrix. + For each element (i,j) that is true, set + + prio = update(prio, srv[j].prio + + where update is max for start sequence and min for stop. + 4. Create symlink name and return new Symlink structure. + */ srvno := rseq.RegisterService(NewRCService(newsrv)) rseq.BuildDependencies() if rseq.Isstart && rseq.Deps.IsSet(srvno, 0) { @@ -560,7 +605,8 @@ func (rseq *RCSeq) Inc(newsrv Service) *Symlink { if ok, diag := srv.Enabled(); !ok { Logger.Printf("%s: %s depends on %s, which is not enabled\n", rseq.dir.runlevel.Letter(), - rseq.Service[srvno].BaseName(), srv.BaseName()); + rseq.Service[srvno].BaseName(), + srv.BaseName()); Logger.Printf("%s\n", diag); return nil } @@ -569,7 +615,7 @@ func (rseq *RCSeq) Inc(newsrv Service) *Symlink { } prio.Incr() // Traverse services that have the same dependencies - next = rseq.TraverseMatchingRows(srvno) + next = rseq.TraverseEquidependentServices(srvno) srv = next() for srv != nil { // fmt.Printf("Traversing: %s %d\n", srv.BaseName(), srv.Priority()) @@ -584,6 +630,8 @@ func (rseq *RCSeq) Inc(newsrv Service) *Symlink { } else { initial = `K` } - return &Symlink{src: filepath.Join(InitDirectory, rseq.Service[srvno].BaseName()), - dst: filepath.Join(rseq.dir.dirname, initial + prio.String() + rseq.Service[srvno].BaseName())} + return &Symlink{src: filepath.Join(InitDirectory, + rseq.Service[srvno].BaseName()), + dst: filepath.Join(rseq.dir.dirname, + initial + prio.String() + rseq.Service[srvno].BaseName())} } |