Skip to content

Commit

Permalink
Add kill command.
Browse files Browse the repository at this point in the history
  • Loading branch information
nao1215 committed Dec 16, 2021
1 parent 4d97cd7 commit a57a24f
Show file tree
Hide file tree
Showing 5 changed files with 339 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file. The format
## [0.30.00] - 2021-12-15
### Added
- chown command.
- kill command.
- wget command.
## [0.29.00] - 2021-12-15
### Added
Expand Down
1 change: 1 addition & 0 deletions docs/introduction/en/CommandAppletList.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
| hostid | Print hostid (Host Identity Number, hex)|
| id | Print User ID and Group ID|
| ischroot| Detect if running in a chroot|
| kill | Kill process or send signal to process|
| ln | Create hard link or symbolic link|
| mbsh | Mimix Box Shell (In development)|
| md5sum| Calculate or Check md5sum message digest|
Expand Down
4 changes: 3 additions & 1 deletion internal/applets/applet.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import (
"github.com/nao1215/mimixbox/internal/applets/shellutils/hostid"
"github.com/nao1215/mimixbox/internal/applets/shellutils/id"
"github.com/nao1215/mimixbox/internal/applets/shellutils/ischroot"
"github.com/nao1215/mimixbox/internal/applets/shellutils/kill"
"github.com/nao1215/mimixbox/internal/applets/shellutils/mbsh"
"github.com/nao1215/mimixbox/internal/applets/shellutils/path"
"github.com/nao1215/mimixbox/internal/applets/shellutils/sddf"
Expand Down Expand Up @@ -101,10 +102,11 @@ func init() {
"false": {false.Run, "Do nothing. Return unsuccess(1)"},
"ghrdc": {ghrdc.Run, "GitHub Relase Download Counter"},
"groups": {groups.Run, "Print the groups to which USERNAME belongs"},
"head": {head.Run, "Print the first NUMBER(default=10) lines"},
"hostid": {hostid.Run, "Print hostid (Host Identity Number, hex)!!!Does not work properly!!!"},
"id": {id.Run, "Print User ID and Group ID"},
"head": {head.Run, "Print the first NUMBER(default=10) lines"},
"ischroot": {ischroot.Run, "Detect if running in a chroot"},
"kill": {kill.Run, "Kill process or send signal to process"},
"ln": {ln.Run, "Create hard or symbolic link"},
"mbsh": {mbsh.Run, "Mimix Box Shell"},
"md5sum": {md5sum.Run, "Calculate or Check md5sum message digest"},
Expand Down
203 changes: 203 additions & 0 deletions internal/applets/shellutils/kill/kill.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
//
// mimixbox/internal/applets/shellutils/kill/kill.go
//
// Copyright 2021 Naohiro CHIKAMATSU
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package kill

import (
"fmt"
"os"
"strconv"
"strings"
"syscall"

mb "github.com/nao1215/mimixbox/internal/lib"
)

const cmdName string = "kill"
const version = "1.0.0"

var osExit = os.Exit

// Exit code
const (
ExitSuccess int = iota // 0
ExitFailuer
)

type options struct {
nameFlg bool
signalName string
numberFlg bool
signalNumber string
listFlg bool
list string
directFlg bool
direct string
}

func Run() (int, error) {
process, opts := parseArgs(os.Args)

valid(process, opts)
if opts.listFlg {
if opts.list == "" {
mb.PrintSignalList()
} else {
mb.PrintSignal(opts.list)
}
osExit(ExitSuccess)
}

return kill(process, opts)
}

func kill(process []string, opts options) (int, error) {
status := ExitSuccess
for _, v := range process {
pid, err := strconv.Atoi(v)
if err != nil {
fmt.Fprintln(os.Stderr, "kill: "+err.Error()+": "+v)
status = ExitFailuer
continue
}

p, err := os.FindProcess(pid)
if err != nil {
fmt.Fprintln(os.Stderr, "kill: "+err.Error()+": "+v)
status = ExitFailuer
continue
}

signal := decideSignal(opts)
err = p.Signal(syscall.Signal(signal))
if err != nil {
fmt.Fprintln(os.Stderr, "kill: "+err.Error())
status = ExitFailuer
continue
}
}
return status, nil
}

func decideSignal(opts options) int32 {
var signal int32 = 0
// TODO: Workaround. I have not investigated the response
// when multiple signals are specified.
if opts.directFlg {
str := strings.TrimLeft(opts.direct, "-")
if len(str) <= 2 {
signal = mb.SignalAtoi(str)
} else {
signal = mb.ConvSignalNameToNum(str)
}
} else if opts.nameFlg {
signal = mb.ConvSignalNameToNum(opts.signalName)
} else if opts.numberFlg {
signal = mb.SignalAtoi(opts.signalNumber)
} else {
signal = 9
}
return signal
}

func valid(process []string, opts options) {
if len(process) == 0 && !opts.listFlg {
showHelp()
osExit(ExitFailuer)
}
if opts.nameFlg && !mb.IsSignalName(opts.signalName) {
fmt.Fprintln(os.Stderr, "kill: -s: invalid signal specification:"+opts.signalName)
osExit(ExitFailuer)
}
if opts.numberFlg && !mb.IsSignalName(opts.signalNumber) {
fmt.Fprintln(os.Stderr, "kill: -n: invalid signal specification:"+opts.signalNumber)
osExit(ExitFailuer)
}

trim := strings.TrimLeft(opts.direct, "-")
if opts.directFlg && !mb.IsSignalName(trim) && !mb.IsSignalNumber(trim) {
fmt.Fprintln(os.Stderr, "kill: "+opts.direct+": invalid signal specification")
osExit(ExitFailuer)
}
}

func parseArgs(args []string) ([]string, options) {
if mb.HasVersionOpt(args) {
mb.ShowVersion(cmdName, version)
osExit(ExitSuccess)
}

if mb.HasHelpOpt(args) {
showHelp()
osExit(ExitSuccess)
}

var opts options = options{false, "", false, "", false, "", false, ""}
args = args[1:]
for i, v := range args {
if v == "-s" {
opts.nameFlg = true
if len(args) > i+1 {
opts.signalName = args[i+1]
} else {
fmt.Fprintln(os.Stderr, "kill: -s: option requires an argument")
osExit(ExitFailuer)
}
continue
} else if v == "-n" {
opts.numberFlg = true
if len(args) > i+1 {
opts.signalNumber = args[i+1]
} else {
fmt.Fprintln(os.Stderr, "kill: -n: option requires an argument")
osExit(ExitFailuer)
}
continue
} else if v == "-l" {
opts.listFlg = true
if len(args) > i+1 {
opts.list = args[i+1]
}
} else if strings.Contains(v, "-") {
opts.directFlg = true
opts.direct = v
}
}
return decideProcess(args[1:], opts), opts
}

func decideProcess(args []string, opts options) []string {
new := mb.Remove(args, "-s")
new = mb.Remove(new, "-n")
new = mb.Remove(new, "-l")
new = mb.Remove(new, opts.list)
new = mb.Remove(new, opts.signalName)
new = mb.Remove(new, opts.signalNumber)
new = mb.Remove(new, opts.direct)
return new
}

func showHelp() {
fmt.Fprintln(os.Stdout, "Usage:")
fmt.Fprintln(os.Stdout, " kill [-s sigspec | -n signum | -sigspec] PID or")
fmt.Fprintln(os.Stdout, " kill -l [PID]")
fmt.Fprintln(os.Stdout, "")
fmt.Fprintln(os.Stdout, "Application Options:")
fmt.Fprintln(os.Stdout, " -v, --version Show kill command version")
fmt.Fprintln(os.Stdout, "")
fmt.Fprintln(os.Stdout, "Help Options:")
fmt.Fprintln(os.Stdout, " -h, --help Show this help message")
}
131 changes: 131 additions & 0 deletions internal/lib/signal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
//
// mimixbox/internal/lib/signal.go
//
// Copyright 2021 Naohiro CHIKAMATSU
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package mb

import (
"fmt"
"os"
"strconv"
"strings"
)

type signal struct {
number string
name string
desc string
}

// [Reference]
// https://www-uxsup.csx.cam.ac.uk/courses/moved.Building/signals.pdf
var Signals []signal = []signal{
{"1", "SIGHUP", "Hangup detected on controlling terminal or death of controlling process"},
{"2", "SIGINT", "The process was interrupted (When user hits Cnrl+C)"},
{"3", "SIGQUIT", "Quit program"},
{"4", "SIGILL", "Illegal instruction"},
{"5", "SIGTRAP", "Trace trap for debugging"},
{"6", "SIGABRT", "Emegency stop. Abort program (formerly SIGIOT)"},
{"7", "SIGBUS", "Bus error. e.g. alignment errors in memory access"},
{"8", "SIGFPE", "A floating point exception happened in the program."},
{"9", "SIGKILL", "Kill program"},
{"10", "SIGUSR1", "Left for the programmers to do whatever they want"},
{"11", "SIGSEGV", "Segmentation violation"},
{"12", "SIGUSR2", "Left for the programmers to do whatever they want"},
{"13", "SIGPIPE", "Write on a pipe with no reader"},
{"14", "SIGALRM", "Real-time timer (request a wake up call) expired"},
{"15", "SIGTERM", "Software termination"},
{"16", "SIGSTKFLT", "Unused (Stack fault in the FPU)"},
{"17", "SIGCHLD", "Stop or exit child process"},
{"18", "SIGCONT", "Restart from stop"},
{"19", "SIGSTOP", "Stop process"},
{"20", "SIGTSTP", "Stop process from terminal (When user hits Cnrl+Z)"},
{"21", "SIGTTIN", "Signal to a backgrounded process when it tries to read input from its terminal"},
{"22", "SIGTTOU", "Signal to a backgrounded process when it tries to write output to its terminal"},
{"23", "SIGURG", "Network connection when urgent out of band data is sent to it"},
{"24", "SIGXCPU", "Exceeded CPU limit"},
{"25", "SIGXFSZ", "Exceeded file size limit"},
{"26", "SIGVTALRM", "Virtual alram cloc"},
{"27", "SIGPROF", "Profiling timer's timeout"},
{"28", "SIGWINCH", "Window resize signal"},
{"29", "SIGIO", "Input / output is possible"},
{"30", "SIGPWR", "Power failure"},
{"31", "SIGSYS", "Unused (Illegal argument to routine)"},
// signal number 33-64 is real-time signal.
// It has no predefined meaning and can be used for application-defined purposes.
}

func IsSignalNumber(num string) bool {
for _, v := range Signals {
if num == v.number {
return true
}
}
return false
}

func IsSignalName(name string) bool {
for _, v := range Signals {
if name == v.name {
return true
}
shortName := strings.TrimPrefix(v.name, "SIG")
if name == shortName {
return true
}
}
return false
}

func SignalAtoi(num string) int32 {
n, err := strconv.Atoi(num)
if err != nil {
return int32(-1)
}
return int32(n)
}

func ConvSignalNameToNum(name string) int32 {
if !strings.HasPrefix(name, "SIG") {
name = "SIG" + name
}
for _, v := range Signals {
if name == v.name {
return SignalAtoi(v.number)
}
}
return int32(-1)
}

func PrintSignalList() {
for _, v := range Signals {
fmt.Fprintf(os.Stdout, "%2s %10s %s\n", v.number, v.name, v.desc)
}
}

func PrintSignal(numOrName string) {
for _, v := range Signals {
if numOrName == v.name {
fmt.Fprintln(os.Stdout, v.number)
}
shortName := strings.TrimPrefix(v.name, "SIG")
if numOrName == shortName {
fmt.Fprintln(os.Stdout, v.number)
}
if numOrName == v.number {
fmt.Fprintln(os.Stdout, strings.TrimPrefix(v.name, "SIG"))
}
}
}

0 comments on commit a57a24f

Please sign in to comment.