aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2019-09-25 15:08:01 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2019-09-25 15:14:08 +0300
commit1da3cd08079eaea4f6ec6bb73c2c22c829cb4449 (patch)
tree55b24aeb2dbfd554f4000fb8ad17f77f0ae9f09c
parent6856c09b11993f35fef72a32c21b166ae183f5d6 (diff)
downloadproto-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.go28
-rwxr-xr-xetc/init.d/D2
-rwxr-xr-xetc/init.d/E2
-rwxr-xr-xetc/init.d/F9
-rw-r--r--main.go146
5 files changed, 118 insertions, 69 deletions
diff --git a/depmap.go b/depmap.go
index 71f3b3c..95e56e2 100644
--- a/depmap.go
+++ b/depmap.go
@@ -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
diff --git a/main.go b/main.go
index 9bf3660..61cd466 100644
--- a/main.go
+++ b/main.go
@@ -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())}
}

Return to:

Send suggestions and report system problems to the System administrator.