Fix only serve/check Secure for runtime
This commit is contained in:
@@ -34,9 +34,6 @@ imgextract --name system ${uri_base}&stage=system&compress=gz || goto noco
|
||||
echo Container image:
|
||||
imgextract --name container ${uri_base}&stage=container&compress=gz || goto reboot
|
||||
|
||||
echo Runtime image:
|
||||
imgextract --name runtime ${uri_base}&stage=runtime&compress=gz || goto reboot
|
||||
|
||||
{{if ne .KernelOverride "" -}}
|
||||
echo Kernel modules image:
|
||||
imgextract --name kmods ${uri_base}&stage=kmods&compress=gz || goto reboot
|
||||
@@ -54,8 +51,6 @@ initrd --name system ${uri_base}&stage=system || goto reboot
|
||||
echo Container image:
|
||||
initrd --name container ${uri_base}&stage=container || goto reboot
|
||||
|
||||
echo Runtime image:
|
||||
initrd --name runtime ${uri_base}&stage=runtime || goto reboot
|
||||
|
||||
{{if ne .KernelOverride "" -}}
|
||||
echo Kernel modules image:
|
||||
@@ -67,13 +62,13 @@ initrd --name kmods ${uri_base}&stage=kmods || goto reboot
|
||||
|
||||
echo Booting in 3s...
|
||||
{{if ne .KernelOverride "" -}}
|
||||
echo boot kernel initrd=container initrd=kmods initrd=system initrd=runtime wwid={{.Hwaddr}} {{.KernelArgs}}
|
||||
echo boot kernel initrd=container initrd=kmods initrd=system wwid={{.Hwaddr}} {{.KernelArgs}}
|
||||
sleep 3
|
||||
boot kernel initrd=container initrd=kmods initrd=system initrd=runtime wwid={{.Hwaddr}} {{.KernelArgs}} || goto reboot
|
||||
boot kernel initrd=container initrd=kmods initrd=system wwid={{.Hwaddr}} {{.KernelArgs}} || goto reboot
|
||||
{{- else -}}
|
||||
echo boot kernel initrd=container initrd=system initrd=runtime wwid={{.Hwaddr}} {{.KernelArgs}}
|
||||
echo boot kernel initrd=container initrd=system wwid={{.Hwaddr}} {{.KernelArgs}}
|
||||
sleep 3
|
||||
boot kernel initrd=container initrd=system initrd=runtime wwid={{.Hwaddr}} {{.KernelArgs}} || goto reboot
|
||||
boot kernel initrd=container initrd=system wwid={{.Hwaddr}} {{.KernelArgs}} || goto reboot
|
||||
{{- end}}
|
||||
|
||||
:reboot
|
||||
|
||||
@@ -41,6 +41,8 @@ func DaemonInitLogging() error {
|
||||
if err == nil {
|
||||
wwlog.SetLogLevel(level)
|
||||
}
|
||||
}else{
|
||||
wwlog.SetLogLevel(wwlog.SERV)
|
||||
}
|
||||
|
||||
conf, err := warewulfconf.New()
|
||||
@@ -57,9 +59,8 @@ func DaemonInitLogging() error {
|
||||
return errors.Wrap(err, "Could not create syslog writer")
|
||||
}
|
||||
|
||||
wwlog.SetLogWriters(logwriter, logwriter)
|
||||
|
||||
wwlog.SetLogFormatter(wwlog.DefaultFormatter)
|
||||
wwlog.SetLogWriters(logwriter, logwriter)
|
||||
|
||||
}
|
||||
|
||||
@@ -81,7 +82,13 @@ func DaemonStart() error {
|
||||
}
|
||||
|
||||
os.Setenv("WAREWULFD_BACKGROUND", "1")
|
||||
os.Setenv("WAREWULFD_LOGLEVEL", strconv.Itoa(wwlog.GetLogLevel()))
|
||||
|
||||
logLevel := wwlog.GetLogLevel()
|
||||
if logLevel == wwlog.INFO {
|
||||
os.Setenv("WAREWULFD_LOGLEVEL", strconv.Itoa(wwlog.SERV))
|
||||
}else{
|
||||
os.Setenv("WAREWULFD_LOGLEVEL", strconv.Itoa(logLevel))
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(WAREWULFD_LOGFILE, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
@@ -105,7 +112,7 @@ func DaemonStart() error {
|
||||
|
||||
fmt.Fprintf(p, "%d", pid)
|
||||
|
||||
wwlog.Info("Started Warewulf (%s) server at PID: %d", version.GetVersion(), pid)
|
||||
wwlog.Serv("Started Warewulf (%s) server at PID: %d", version.GetVersion(), pid)
|
||||
|
||||
}
|
||||
|
||||
@@ -131,7 +138,7 @@ func DaemonStatus() error {
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to send process SIGCONT")
|
||||
} else {
|
||||
wwlog.Info("Warewulf server is running at PID: %d", pid)
|
||||
wwlog.Serv("Warewulf server is running at PID: %d", pid)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +166,12 @@ func DaemonReload() error {
|
||||
}
|
||||
}
|
||||
|
||||
os.Setenv("WAREWULFD_LOGLEVEL", strconv.Itoa(wwlog.GetLogLevel()))
|
||||
logLevel := wwlog.GetLogLevel()
|
||||
if logLevel == wwlog.INFO {
|
||||
os.Setenv("WAREWULFD_LOGLEVEL", strconv.Itoa(wwlog.SERV))
|
||||
}else{
|
||||
os.Setenv("WAREWULFD_LOGLEVEL", strconv.Itoa(logLevel))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -187,7 +199,7 @@ func DaemonStop() error {
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to send process SIGTERM")
|
||||
} else {
|
||||
wwlog.Info("Terminated Warewulf server at PID: %d", pid)
|
||||
wwlog.Serv("Terminated Warewulf server at PID: %d", pid)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,9 +49,11 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if conf.Warewulf.Secure {
|
||||
wwlog.Recv("hwaddr: %s, ipaddr: %s, stage: %s", rinfo.hwaddr, req.RemoteAddr, rinfo.stage )
|
||||
|
||||
if rinfo.stage == "runtime" && conf.Warewulf.Secure {
|
||||
if rinfo.remoteport >= 1024 {
|
||||
wwlog.Error("DENIED: Non-privledged port: %s", req.RemoteAddr)
|
||||
wwlog.Denied("Non-privledged port: %s", req.RemoteAddr)
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
@@ -64,8 +66,6 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) {
|
||||
"system": "SYSTEM_OVERLAY",
|
||||
"runtime": "RUNTIME_OVERLAY" }
|
||||
|
||||
wwlog.Info("REQ: %s: %s", rinfo.hwaddr, rinfo.stage )
|
||||
|
||||
status_stage := status_stages[rinfo.stage]
|
||||
var stage_overlays []string
|
||||
var stage_file string = ""
|
||||
@@ -77,7 +77,7 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) {
|
||||
// If we failed to find a node, let's see if we can add one...
|
||||
var netdev string
|
||||
|
||||
wwlog.Info("%s (node not configured)", rinfo.hwaddr)
|
||||
wwlog.Warn("%s (node not configured)", rinfo.hwaddr)
|
||||
|
||||
nodeDB, err := nodepkg.New()
|
||||
if err != nil {
|
||||
@@ -92,18 +92,18 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) {
|
||||
n.Discoverable.SetB(false)
|
||||
err := nodeDB.NodeUpdate(n)
|
||||
if err != nil {
|
||||
wwlog.Info("%s (failed to set node configuration)", rinfo.hwaddr)
|
||||
wwlog.Serv("%s (failed to set node configuration)", rinfo.hwaddr)
|
||||
|
||||
} else {
|
||||
err := nodeDB.Persist()
|
||||
if err != nil {
|
||||
wwlog.Info("%s (failed to persist node configuration)", rinfo.hwaddr)
|
||||
wwlog.Serv("%s (failed to persist node configuration)", rinfo.hwaddr)
|
||||
|
||||
} else {
|
||||
node = n
|
||||
_ = overlay.BuildAllOverlays([]nodepkg.NodeInfo{n})
|
||||
|
||||
wwlog.Info("%s (node automatically configured)", rinfo.hwaddr)
|
||||
wwlog.Serv("%s (node automatically configured)", rinfo.hwaddr)
|
||||
|
||||
err := LoadNodeDB()
|
||||
if err != nil {
|
||||
@@ -117,7 +117,7 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) {
|
||||
|
||||
if node.AssetKey.Defined() && node.AssetKey.Get() != rinfo.assetkey {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
wwlog.Error("Incorrect asset key for node: %s", node.Id.Get())
|
||||
wwlog.Denied("Incorrect asset key for node: %s", node.Id.Get())
|
||||
updateStatus(node.Id.Get(), status_stage, "BAD_ASSET", rinfo.ipaddr)
|
||||
return
|
||||
}
|
||||
@@ -199,13 +199,13 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) {
|
||||
oneoverlaynewer = oneoverlaynewer || util.PathIsNewer(stage_file, overlay.OverlaySourceDir(overlayname))
|
||||
}
|
||||
if !util.IsFile(stage_file) || util.PathIsNewer(stage_file, nodepkg.ConfigFile) || oneoverlaynewer {
|
||||
wwlog.Info("BUILD %15s, overlays %v", node.Id.Get(), stage_overlays)
|
||||
wwlog.Serv("BUILD %15s, overlays %v", node.Id.Get(), stage_overlays)
|
||||
_ = overlay.BuildOverlay(node, stage_overlays)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wwlog.Info("stage_file '%s'", stage_file )
|
||||
wwlog.Serv("stage_file '%s'", stage_file )
|
||||
|
||||
if util.IsFile(stage_file) {
|
||||
|
||||
@@ -241,7 +241,7 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) {
|
||||
wwlog.ErrorExc(err, "")
|
||||
}
|
||||
|
||||
wwlog.Info("SEND: %15s: %s", node.Id.Get(), stage_file)
|
||||
wwlog.Send("%15s: %s", node.Id.Get(), stage_file)
|
||||
|
||||
}else{
|
||||
if rinfo.compress == "gz" {
|
||||
|
||||
@@ -48,7 +48,7 @@ func sendFile(w http.ResponseWriter, filename string, sendto string) error {
|
||||
w.Header().Set("Content-Length", FileSize)
|
||||
_, err = buf.WriteTo(w)
|
||||
|
||||
wwlog.Info("SEND: %15s: %s", sendto, filename)
|
||||
wwlog.Send("%15s: %s", sendto, filename)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ func RunServer() error {
|
||||
}
|
||||
|
||||
daemonPort := conf.Warewulf.Port
|
||||
wwlog.Info("Starting HTTPD REST service on port %d", daemonPort)
|
||||
wwlog.Serv("Starting HTTPD REST service on port %d", daemonPort)
|
||||
|
||||
err = http.ListenAndServe(":"+strconv.Itoa(daemonPort), nil)
|
||||
if err != nil {
|
||||
|
||||
@@ -8,22 +8,7 @@ import (
|
||||
"time"
|
||||
"runtime"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
const (
|
||||
SECCRITICAL = 51
|
||||
CRITICAL = 50
|
||||
SECERROR = 41
|
||||
ERROR = 40
|
||||
SECWARN = 31
|
||||
WARN = 30
|
||||
SECINFO = 21
|
||||
INFO = 20
|
||||
SECVERBOSE = 16
|
||||
VERBOSE = 15
|
||||
SECDEBUG = 11
|
||||
DEBUG = 10
|
||||
NOTSET = 0
|
||||
"sort"
|
||||
)
|
||||
|
||||
type LogRecord struct {
|
||||
@@ -39,52 +24,78 @@ type LogRecord struct {
|
||||
|
||||
/*
|
||||
Format a log message from a record
|
||||
rec.level >= logLevel
|
||||
Only called if rec.level >= logLevel
|
||||
*/
|
||||
type LogFormatter func(logLevel int, rec *LogRecord) string
|
||||
|
||||
/*
|
||||
Get string level name for level number
|
||||
*/
|
||||
func LevelName(level int) string {
|
||||
if level >= SECCRITICAL {
|
||||
return "SECCRITICAL"
|
||||
}
|
||||
if level >= CRITICAL {
|
||||
return "CRITICAL"
|
||||
}
|
||||
if level >= SECERROR {
|
||||
return "SECERROR"
|
||||
}
|
||||
if level >= ERROR {
|
||||
return "ERROR"
|
||||
}
|
||||
if level >= SECWARN {
|
||||
return "SECWARN"
|
||||
}
|
||||
if level >= WARN {
|
||||
return "WARN"
|
||||
}
|
||||
if level >= SECINFO {
|
||||
return "SECINFO"
|
||||
}
|
||||
if level >= INFO {
|
||||
return "INFO"
|
||||
}
|
||||
if level >= SECVERBOSE {
|
||||
return "SECVERBOSE"
|
||||
}
|
||||
if level >= VERBOSE {
|
||||
return "VERBOSE"
|
||||
}
|
||||
if level >= SECDEBUG {
|
||||
return "SECDEBUG"
|
||||
}
|
||||
if level >= DEBUG {
|
||||
return "DEBUG"
|
||||
}
|
||||
var (
|
||||
SECCRITICAL = SetLevelName(51, "SECCRITICAL")
|
||||
CRITICAL = SetLevelName(50, "CRITICAL")
|
||||
DENIED = SetLevelName(42, "DENIED")
|
||||
SECERROR = SetLevelName(41, "SECERROR")
|
||||
ERROR = SetLevelName(40, "ERROR")
|
||||
SECWARN = SetLevelName(31, "SECWARN")
|
||||
WARN = SetLevelName(30, "WARN")
|
||||
SEND = SetLevelName(27, "SEND")
|
||||
RECV = SetLevelName(26, "RECV")
|
||||
SERV = SetLevelName(25, "SERV")
|
||||
SECINFO = SetLevelName(21, "SECINFO")
|
||||
INFO = SetLevelName(20, "INFO")
|
||||
SECVERBOSE = SetLevelName(16, "SECVERBOSE")
|
||||
VERBOSE = SetLevelName(15, "VERBOSE")
|
||||
SECDEBUG = SetLevelName(11, "SECDEBUG")
|
||||
DEBUG = SetLevelName(10, "DEBUG")
|
||||
)
|
||||
|
||||
return "NOTSET"
|
||||
var levelNums = []int{0}
|
||||
var levelNames = []string{"NOTSET"}
|
||||
var logLevel = INFO
|
||||
var logOut io.Writer = os.Stdout
|
||||
var logErr io.Writer = os.Stderr
|
||||
var logFormatter LogFormatter = DefaultFormatter
|
||||
|
||||
func LevelNameEff(level int) (int, int, string) {
|
||||
n := len(levelNums)
|
||||
idx := sort.SearchInts(levelNums, level)
|
||||
|
||||
if idx >= n {
|
||||
idx = n-1
|
||||
}
|
||||
|
||||
eff_level := levelNums[idx]
|
||||
eff_name := levelNames[idx]
|
||||
|
||||
return idx, eff_level, eff_name
|
||||
}
|
||||
|
||||
func LevelName(level int) string {
|
||||
|
||||
_, _, name := LevelNameEff(level)
|
||||
return name
|
||||
}
|
||||
|
||||
func SetLevelName(level int, name string) int {
|
||||
n := len(levelNums)
|
||||
idx := sort.SearchInts(levelNums, level)
|
||||
|
||||
if idx < n && levelNums[idx] == level {
|
||||
levelNames[idx] = name
|
||||
|
||||
}else{
|
||||
|
||||
levelNums = append(levelNums, level)
|
||||
levelNames = append(levelNames, name)
|
||||
|
||||
if idx < n {
|
||||
copy(levelNums[idx+1:], levelNums[idx:])
|
||||
copy(levelNames[idx+1:], levelNames[idx:])
|
||||
|
||||
levelNums[idx] = level
|
||||
levelNames[idx] = name
|
||||
}
|
||||
}
|
||||
|
||||
return level
|
||||
}
|
||||
|
||||
func DefaultFormatter(logLevel int, rec *LogRecord) string {
|
||||
@@ -96,7 +107,7 @@ func DefaultFormatter(logLevel int, rec *LogRecord) string {
|
||||
}
|
||||
|
||||
if rec.Err != nil {
|
||||
if logLevel <= VERBOSE {
|
||||
if logLevel < VERBOSE {
|
||||
// when debugging errors, add file and line number, and any stack trace
|
||||
message += fmt.Sprintf("%s:%d\n%+v\n", rec.File, rec.Line, rec.Err )
|
||||
|
||||
@@ -113,13 +124,15 @@ func DefaultFormatter(logLevel int, rec *LogRecord) string {
|
||||
return message
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%-7s: %s", LevelName(rec.Level), message)
|
||||
name := LevelName(rec.Level)
|
||||
|
||||
if len(name) <= 7 {
|
||||
return fmt.Sprintf("%-7s: %s", name, message)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%-11s: %s", name, message)
|
||||
}
|
||||
|
||||
var logLevel = INFO
|
||||
var logOut io.Writer = os.Stdout
|
||||
var logErr io.Writer = os.Stderr
|
||||
var logFormatter LogFormatter = DefaultFormatter
|
||||
|
||||
func EnabledForLevel(level int) bool {
|
||||
return level >= logLevel
|
||||
@@ -169,10 +182,10 @@ func GetLogFormatter() LogFormatter {
|
||||
/*
|
||||
Internal method to create a log record
|
||||
*/
|
||||
func recordLog(level int, err error, message string, a ...interface{}) {
|
||||
func LogCaller(level int, skip int, err error, message string, a ...interface{}) {
|
||||
|
||||
if EnabledForLevel(level) {
|
||||
pc, file, line, ok := runtime.Caller(2)
|
||||
pc, file, line, ok := runtime.Caller(skip + 1)
|
||||
if !ok {
|
||||
file = "[unknown]"
|
||||
}
|
||||
@@ -198,92 +211,108 @@ func recordLog(level int, err error, message string, a ...interface{}) {
|
||||
}
|
||||
|
||||
func Println(level int, message string) {
|
||||
recordLog(level, nil, message)
|
||||
LogCaller(level, 1, nil, message)
|
||||
}
|
||||
|
||||
func Printf(level int, message string, a ...interface{}) {
|
||||
recordLog(level, nil, message, a...)
|
||||
LogCaller(level, 1, nil, message, a...)
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
Named log level functions
|
||||
*/
|
||||
func Log(level int, message string, a ...interface{}) {
|
||||
recordLog(level, nil, message, a...)
|
||||
LogCaller(level, 1, nil, message, a...)
|
||||
}
|
||||
|
||||
func LogExc(level int, err error, message string, a ...interface{}) {
|
||||
recordLog(level, err, message, a...)
|
||||
LogCaller(level, 1, err, message, a...)
|
||||
}
|
||||
|
||||
func Debug(message string, a ...interface{}) {
|
||||
recordLog(DEBUG, nil, message, a...)
|
||||
LogCaller(DEBUG, 1, nil, message, a...)
|
||||
}
|
||||
|
||||
func DebugExc(err error, message string, a ...interface{}) {
|
||||
recordLog(DEBUG, err, message, a...)
|
||||
LogCaller(DEBUG, 1, err, message, a...)
|
||||
}
|
||||
|
||||
func SecDebug(message string, a ...interface{}) {
|
||||
recordLog(SECDEBUG, nil, message, a...)
|
||||
LogCaller(SECDEBUG, 1, nil, message, a...)
|
||||
}
|
||||
|
||||
func Verbose(message string, a ...interface{}) {
|
||||
recordLog(VERBOSE, nil, message, a...)
|
||||
LogCaller(VERBOSE, 1, nil, message, a...)
|
||||
}
|
||||
|
||||
func VerboseExc(err error, message string, a ...interface{}) {
|
||||
recordLog(VERBOSE, err, message, a...)
|
||||
LogCaller(VERBOSE, 1, err, message, a...)
|
||||
}
|
||||
|
||||
func SecVerbose(message string, a ...interface{}) {
|
||||
recordLog(SECVERBOSE, nil, message, a...)
|
||||
LogCaller(SECVERBOSE, 1, nil, message, a...)
|
||||
}
|
||||
|
||||
func Info(message string, a ...interface{}) {
|
||||
recordLog(INFO, nil, message, a...)
|
||||
LogCaller(INFO, 1, nil, message, a...)
|
||||
}
|
||||
|
||||
func InfoExc(err error, message string, a ...interface{}) {
|
||||
recordLog(INFO, err, message, a...)
|
||||
LogCaller(INFO, 1, err, message, a...)
|
||||
}
|
||||
|
||||
func SecInfo(message string, a ...interface{}) {
|
||||
recordLog(SECINFO, nil, message, a...)
|
||||
LogCaller(SECINFO, 1, nil, message, a...)
|
||||
}
|
||||
|
||||
func Serv(message string, a ...interface{}) {
|
||||
LogCaller(SERV, 1, nil, message, a...)
|
||||
}
|
||||
|
||||
func Recv(message string, a ...interface{}) {
|
||||
LogCaller(RECV, 1, nil, message, a...)
|
||||
}
|
||||
|
||||
func Send(message string, a ...interface{}) {
|
||||
LogCaller(SEND, 1, nil, message, a...)
|
||||
}
|
||||
|
||||
func Warn(message string, a ...interface{}) {
|
||||
recordLog(WARN, nil, message, a...)
|
||||
LogCaller(WARN, 1, nil, message, a...)
|
||||
}
|
||||
|
||||
func WarnExc(err error, message string, a ...interface{}) {
|
||||
recordLog(WARN, err, message, a...)
|
||||
LogCaller(WARN, 1, err, message, a...)
|
||||
}
|
||||
|
||||
func SecWarn(message string, a ...interface{}) {
|
||||
recordLog(SECWARN, nil, message, a...)
|
||||
LogCaller(SECWARN, 1, nil, message, a...)
|
||||
}
|
||||
|
||||
func Error(message string, a ...interface{}) {
|
||||
recordLog(ERROR, nil, message, a...)
|
||||
LogCaller(ERROR, 1, nil, message, a...)
|
||||
}
|
||||
|
||||
func ErrorExc(err error, message string, a ...interface{}) {
|
||||
recordLog(ERROR, err, message, a...)
|
||||
LogCaller(ERROR, 1, err, message, a...)
|
||||
}
|
||||
|
||||
func SecError(message string, a ...interface{}) {
|
||||
recordLog(SECERROR, nil, message, a...)
|
||||
LogCaller(SECERROR, 1, nil, message, a...)
|
||||
}
|
||||
|
||||
func Denied(message string, a ...interface{}) {
|
||||
LogCaller(DENIED, 1, nil, message, a...)
|
||||
}
|
||||
|
||||
func Critical(message string, a ...interface{}) {
|
||||
recordLog(CRITICAL, nil, message, a...)
|
||||
LogCaller(CRITICAL, 1, nil, message, a...)
|
||||
}
|
||||
|
||||
func CriticalExc(err error, message string, a ...interface{}) {
|
||||
recordLog(CRITICAL, err, message, a...)
|
||||
LogCaller(CRITICAL, 1, err, message, a...)
|
||||
}
|
||||
|
||||
func SecCritical(message string, a ...interface{}) {
|
||||
recordLog(SECCRITICAL, nil, message, a...)
|
||||
LogCaller(SECCRITICAL, 1, nil, message, a...)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user