github.com/stolowski/snapd@v0.0.0-20210407085831-115137ce5a22/cmd/snap-update-ns/bootstrap.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2017 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package main 21 22 // Use a pre-main helper to switch the mount namespace. This is required as 23 // golang creates threads at will and setns(..., CLONE_NEWNS) fails if any 24 // threads apart from the main thread exist. 25 26 /* 27 28 #include <stdlib.h> 29 #include "bootstrap.h" 30 31 // The bootstrap function is called by the loader before passing 32 // control to main. We are using `preinit_array` rather than 33 // `init_array` because the Go linker adds its own initialisation 34 // function to `init_array`, and having ours run second would defeat 35 // the purpose of the C bootstrap code. 36 // 37 // The `used` attribute ensures that the compiler doesn't oprimise out 38 // the variable on the mistaken belief that it isn't used. 39 __attribute__((section(".preinit_array"), used)) static typeof(&bootstrap) init = &bootstrap; 40 41 // NOTE: do not add anything before the following `import "C"' 42 */ 43 import "C" 44 45 import ( 46 "errors" 47 "fmt" 48 "syscall" 49 "unsafe" 50 ) 51 52 var ( 53 // ErrNoNamespace is returned when a snap namespace does not exist. 54 ErrNoNamespace = errors.New("cannot update mount namespace that was not created yet") 55 ) 56 57 // IMPORTANT: all the code in this section may be run with elevated privileges 58 // when invoking snap-update-ns from the setuid snap-confine. 59 60 // BootstrapError returns error (if any) encountered in pre-main C code. 61 func BootstrapError() error { 62 if C.bootstrap_msg == nil { 63 return nil 64 } 65 errno := syscall.Errno(C.bootstrap_errno) 66 // Translate EINVAL from setns or ENOENT from open into a dedicated error. 67 if errno == syscall.EINVAL || errno == syscall.ENOENT { 68 return ErrNoNamespace 69 } 70 if errno != 0 { 71 return fmt.Errorf("%s: %s", C.GoString(C.bootstrap_msg), errno) 72 } 73 return fmt.Errorf("%s", C.GoString(C.bootstrap_msg)) 74 } 75 76 // This function is here to make clearing the boostrap errors accessible 77 // from the tests. 78 func clearBootstrapError() { 79 C.bootstrap_msg = nil 80 C.bootstrap_errno = 0 81 } 82 83 // END IMPORTANT 84 85 func makeArgv(args []string) []*C.char { 86 // Create argv array with terminating NULL element 87 argv := make([]*C.char, len(args)+1) 88 for i, arg := range args { 89 argv[i] = C.CString(arg) 90 } 91 return argv 92 } 93 94 func freeArgv(argv []*C.char) { 95 for _, arg := range argv { 96 C.free(unsafe.Pointer(arg)) 97 } 98 } 99 100 // validateInstanceName checks if snap instance name is valid. 101 // This also sets bootstrap_msg on failure. 102 // 103 // This function is here only to make the C.validate_instance_name 104 // code testable from go. 105 func validateInstanceName(instanceName string) int { 106 cStr := C.CString(instanceName) 107 defer C.free(unsafe.Pointer(cStr)) 108 return int(C.validate_instance_name(cStr)) 109 } 110 111 // processArguments parses commnad line arguments. 112 // The argument cmdline is a string with embedded 113 // NUL bytes, separating particular arguments. 114 // 115 // This function is here only to make the C.validate_instance_name 116 // code testable from go. 117 func processArguments(args []string) (snapName string, shouldSetNs bool, processUserFstab bool, uid uint) { 118 argv := makeArgv(args) 119 defer freeArgv(argv) 120 121 var snapNameOut *C.char 122 var shouldSetNsOut C.bool 123 var processUserFstabOut C.bool 124 var uidOut C.ulong 125 C.process_arguments(C.int(len(args)), &argv[0], &snapNameOut, &shouldSetNsOut, &processUserFstabOut, &uidOut) 126 if snapNameOut != nil { 127 snapName = C.GoString(snapNameOut) 128 } 129 shouldSetNs = bool(shouldSetNsOut) 130 processUserFstab = bool(processUserFstabOut) 131 uid = uint(uidOut) 132 133 return snapName, shouldSetNs, processUserFstab, uid 134 }