github.com/anonymouse64/snapd@v0.0.0-20210824153203-04c4c42d842d/cmd/libsnap-confine-private/locking.c (about) 1 /* 2 * Copyright (C) 2017-2018 Canonical Ltd 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 3 as 6 * published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 * 16 */ 17 18 #ifdef HAVE_CONFIG_H 19 #include "config.h" 20 #endif 21 22 #include "locking.h" 23 24 #include <errno.h> 25 #include <fcntl.h> 26 #include <signal.h> 27 #include <stdarg.h> 28 #include <sys/file.h> 29 #include <sys/stat.h> 30 #include <sys/types.h> 31 #include <unistd.h> 32 33 #include "../libsnap-confine-private/cleanup-funcs.h" 34 #include "../libsnap-confine-private/string-utils.h" 35 #include "../libsnap-confine-private/utils.h" 36 37 // SANITY_TIMEOUT is the timeout in seconds that is used when 38 // "sc_enable_sanity_timeout()" is called 39 static const int SANITY_TIMEOUT = 30; 40 41 /** 42 * Flag indicating that a sanity timeout has expired. 43 **/ 44 static volatile sig_atomic_t sanity_timeout_expired = 0; 45 46 /** 47 * Signal handler for SIGALRM that sets sanity_timeout_expired flag to 1. 48 **/ 49 static void sc_SIGALRM_handler(int signum) 50 { 51 sanity_timeout_expired = 1; 52 } 53 54 void sc_enable_sanity_timeout(void) 55 { 56 sanity_timeout_expired = 0; 57 struct sigaction act = {.sa_handler = sc_SIGALRM_handler }; 58 if (sigemptyset(&act.sa_mask) < 0) { 59 die("cannot initialize POSIX signal set"); 60 } 61 // NOTE: we are using sigaction so that we can explicitly control signal 62 // flags and *not* pass the SA_RESTART flag. The intent is so that any 63 // system call we may be sleeping on to gets interrupted. 64 act.sa_flags = 0; 65 if (sigaction(SIGALRM, &act, NULL) < 0) { 66 die("cannot install signal handler for SIGALRM"); 67 } 68 alarm(SANITY_TIMEOUT); 69 debug("sanity timeout initialized and set for %i seconds", 70 SANITY_TIMEOUT); 71 } 72 73 void sc_disable_sanity_timeout(void) 74 { 75 if (sanity_timeout_expired) { 76 die("sanity timeout expired"); 77 } 78 alarm(0); 79 struct sigaction act = {.sa_handler = SIG_DFL }; 80 if (sigemptyset(&act.sa_mask) < 0) { 81 die("cannot initialize POSIX signal set"); 82 } 83 if (sigaction(SIGALRM, &act, NULL) < 0) { 84 die("cannot uninstall signal handler for SIGALRM"); 85 } 86 debug("sanity timeout reset and disabled"); 87 } 88 89 #define SC_LOCK_DIR "/run/snapd/lock" 90 91 static const char *sc_lock_dir = SC_LOCK_DIR; 92 93 static int get_lock_directory(void) 94 { 95 // Create (if required) and open the lock directory. 96 debug("creating lock directory %s (if missing)", sc_lock_dir); 97 sc_identity old = sc_set_effective_identity(sc_root_group_identity()); 98 if (sc_nonfatal_mkpath(sc_lock_dir, 0755) < 0) { 99 die("cannot create lock directory %s", sc_lock_dir); 100 } 101 debug("opening lock directory %s", sc_lock_dir); 102 int dir_fd = 103 open(sc_lock_dir, O_DIRECTORY | O_PATH | O_CLOEXEC | O_NOFOLLOW); 104 (void)sc_set_effective_identity(old); 105 if (dir_fd < 0) { 106 die("cannot open lock directory"); 107 } 108 return dir_fd; 109 } 110 111 static void get_lock_name(char *lock_fname, size_t size, const char *scope, 112 uid_t uid) 113 { 114 if (uid == 0) { 115 // The root user doesn't have a per-user mount namespace. 116 // Doing so would be confusing for services which use $SNAP_DATA 117 // as home, and not in $SNAP_USER_DATA. 118 sc_must_snprintf(lock_fname, size, "%s.lock", scope ? : ""); 119 } else { 120 sc_must_snprintf(lock_fname, size, "%s.%d.lock", 121 scope ? : "", uid); 122 } 123 } 124 125 static int open_lock(const char *scope, uid_t uid) 126 { 127 int dir_fd SC_CLEANUP(sc_cleanup_close) = -1; 128 char lock_fname[PATH_MAX] = { 0 }; 129 int lock_fd; 130 131 dir_fd = get_lock_directory(); 132 get_lock_name(lock_fname, sizeof lock_fname, scope, uid); 133 134 // Open the lock file and acquire an exclusive lock. 135 debug("opening lock file: %s/%s", sc_lock_dir, lock_fname); 136 sc_identity old = sc_set_effective_identity(sc_root_group_identity()); 137 lock_fd = openat(dir_fd, lock_fname, 138 O_CREAT | O_RDWR | O_CLOEXEC | O_NOFOLLOW, 0600); 139 (void)sc_set_effective_identity(old); 140 if (lock_fd < 0) { 141 die("cannot open lock file: %s/%s", sc_lock_dir, lock_fname); 142 } 143 return lock_fd; 144 } 145 146 static int sc_lock_generic(const char *scope, uid_t uid) 147 { 148 int lock_fd = open_lock(scope, uid); 149 sc_enable_sanity_timeout(); 150 debug("acquiring exclusive lock (scope %s, uid %d)", 151 scope ? : "(global)", uid); 152 if (flock(lock_fd, LOCK_EX) < 0) { 153 sc_disable_sanity_timeout(); 154 close(lock_fd); 155 die("cannot acquire exclusive lock (scope %s, uid %d)", 156 scope ? : "(global)", uid); 157 } else { 158 sc_disable_sanity_timeout(); 159 } 160 return lock_fd; 161 } 162 163 int sc_lock_global(void) 164 { 165 return sc_lock_generic(NULL, 0); 166 } 167 168 int sc_lock_snap(const char *snap_name) 169 { 170 return sc_lock_generic(snap_name, 0); 171 } 172 173 void sc_verify_snap_lock(const char *snap_name) 174 { 175 int lock_fd, retval; 176 177 lock_fd = open_lock(snap_name, 0); 178 debug("trying to verify whether exclusive lock over snap %s is held", 179 snap_name); 180 retval = flock(lock_fd, LOCK_EX | LOCK_NB); 181 if (retval == 0) { 182 /* We managed to grab the lock, the lock was not held! */ 183 flock(lock_fd, LOCK_UN); 184 close(lock_fd); 185 errno = 0; 186 die("unexpectedly managed to acquire exclusive lock over snap %s", snap_name); 187 } 188 if (retval < 0 && errno != EWOULDBLOCK) { 189 die("cannot verify exclusive lock over snap %s", snap_name); 190 } 191 /* We tried but failed to grab the lock because the file is already locked. 192 * Good, this is what we expected. */ 193 } 194 195 int sc_lock_snap_user(const char *snap_name, uid_t uid) 196 { 197 return sc_lock_generic(snap_name, uid); 198 } 199 200 void sc_unlock(int lock_fd) 201 { 202 // Release the lock and finish. 203 debug("releasing lock %d", lock_fd); 204 if (flock(lock_fd, LOCK_UN) < 0) { 205 die("cannot release lock %d", lock_fd); 206 } 207 close(lock_fd); 208 }