package container import ( "encoding" "strconv" "sync" . "syscall" "unsafe" "hakurei.app/container/std" ) // Prctl manipulates various aspects of the behavior of the calling thread or process. func Prctl(op, arg2, arg3 uintptr) error { r, _, errno := Syscall(SYS_PRCTL, op, arg2, arg3) if r < 0 { return errno } return nil } // SetPtracer allows processes to ptrace(2) the calling process. func SetPtracer(pid uintptr) error { return Prctl(PR_SET_PTRACER, pid, 0) } // linux/sched/coredump.h const ( SUID_DUMP_DISABLE = iota SUID_DUMP_USER ) // SetDumpable sets the "dumpable" attribute of the calling process. func SetDumpable(dumpable uintptr) error { return Prctl(PR_SET_DUMPABLE, dumpable, 0) } // SetNoNewPrivs sets the calling thread's no_new_privs attribute. func SetNoNewPrivs() error { return Prctl(PR_SET_NO_NEW_PRIVS, 1, 0) } // Isatty tests whether a file descriptor refers to a terminal. func Isatty(fd int) bool { var buf [8]byte r, _, _ := Syscall( SYS_IOCTL, uintptr(fd), TIOCGWINSZ, uintptr(unsafe.Pointer(&buf[0])), ) return r == 0 } // SchedPolicy denotes a scheduling policy defined in include/uapi/linux/sched.h. type SchedPolicy int // include/uapi/linux/sched.h const ( SCHED_NORMAL SchedPolicy = iota SCHED_FIFO SCHED_RR SCHED_BATCH _SCHED_ISO // SCHED_ISO: reserved but not implemented yet SCHED_IDLE SCHED_DEADLINE SCHED_EXT _SCHED_LAST SchedPolicy = iota - 1 ) var _ encoding.TextMarshaler = _SCHED_LAST var _ encoding.TextUnmarshaler = new(SchedPolicy) // String returns a unique representation of policy, also used in encoding. func (policy SchedPolicy) String() string { switch policy { case SCHED_NORMAL: return "" case SCHED_FIFO: return "fifo" case SCHED_RR: return "rr" case SCHED_BATCH: return "batch" case SCHED_IDLE: return "idle" case SCHED_DEADLINE: return "deadline" case SCHED_EXT: return "ext" default: return "invalid policy " + strconv.Itoa(int(policy)) } } // MarshalText performs bounds checking and returns the result of String. func (policy SchedPolicy) MarshalText() ([]byte, error) { if policy == _SCHED_ISO || policy < 0 || policy > _SCHED_LAST { return nil, EINVAL } return []byte(policy.String()), nil } // InvalidSchedPolicyError is an invalid string representation of a [SchedPolicy]. type InvalidSchedPolicyError string func (InvalidSchedPolicyError) Unwrap() error { return EINVAL } func (e InvalidSchedPolicyError) Error() string { return "invalid scheduling policy " + strconv.Quote(string(e)) } // UnmarshalText is the inverse of MarshalText. func (policy *SchedPolicy) UnmarshalText(text []byte) error { switch string(text) { case "fifo": *policy = SCHED_FIFO case "rr": *policy = SCHED_RR case "batch": *policy = SCHED_BATCH case "idle": *policy = SCHED_IDLE case "deadline": *policy = SCHED_DEADLINE case "ext": *policy = SCHED_EXT case "": *policy = 0 return nil default: return InvalidSchedPolicyError(text) } return nil } // for sched_get_priority_max and sched_get_priority_min var ( schedPriority [_SCHED_LAST + 1][2]std.Int schedPriorityErr [_SCHED_LAST + 1][2]error schedPriorityOnce [_SCHED_LAST + 1][2]sync.Once ) // GetPriorityMax returns the maximum priority value that can be used with the // scheduling algorithm identified by policy. func (policy SchedPolicy) GetPriorityMax() (std.Int, error) { schedPriorityOnce[policy][0].Do(func() { priority, _, errno := Syscall( SYS_SCHED_GET_PRIORITY_MAX, uintptr(policy), 0, 0, ) schedPriority[policy][0] = std.Int(priority) if schedPriority[policy][0] < 0 { schedPriorityErr[policy][0] = errno } }) return schedPriority[policy][0], schedPriorityErr[policy][0] } // GetPriorityMin returns the minimum priority value that can be used with the // scheduling algorithm identified by policy. func (policy SchedPolicy) GetPriorityMin() (std.Int, error) { schedPriorityOnce[policy][1].Do(func() { priority, _, errno := Syscall( SYS_SCHED_GET_PRIORITY_MIN, uintptr(policy), 0, 0, ) schedPriority[policy][1] = std.Int(priority) if schedPriority[policy][1] < 0 { schedPriorityErr[policy][1] = errno } }) return schedPriority[policy][1], schedPriorityErr[policy][1] } // schedParam is equivalent to struct sched_param from include/linux/sched.h. type schedParam struct { // sched_priority priority std.Int } // schedSetscheduler sets both the scheduling policy and parameters for the // thread whose ID is specified in tid. If tid equals zero, the scheduling // policy and parameters of the calling thread will be set. // // This function is unexported because it is [very subtle to use correctly]. The // function signature in libc is misleading: pid actually refers to a thread ID. // The glibc wrapper for this system call ignores this semantic and exposes // this counterintuitive behaviour. // // This function is only called from the container setup thread. Do not reuse // this if you do not have something similar in place! // // [very subtle to use correctly]: https://www.openwall.com/lists/musl/2016/03/01/4 func schedSetscheduler(tid int, policy SchedPolicy, param *schedParam) error { if r, _, errno := Syscall( SYS_SCHED_SETSCHEDULER, uintptr(tid), uintptr(policy), uintptr(unsafe.Pointer(param)), ); r < 0 { return errno } return nil } // IgnoringEINTR makes a function call and repeats it if it returns an // EINTR error. This appears to be required even though we install all // signal handlers with SA_RESTART: see #22838, #38033, #38836, #40846. // Also #20400 and #36644 are issues in which a signal handler is // installed without setting SA_RESTART. None of these are the common case, // but there are enough of them that it seems that we can't avoid // an EINTR loop. func IgnoringEINTR(fn func() error) error { for { err := fn() if err != EINTR { return err } } }