-
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
339 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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")) | ||
} | ||
} | ||
} |