1 /* 2 * Copyright (c) 2009 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include "hammer.h" 36 #include <sys/types.h> 37 #include <sys/mount.h> 38 #include <sys/stat.h> 39 #include <unistd.h> 40 #include <string.h> 41 #include <time.h> 42 43 static void config_get(const char *dirpath, struct hammer_ioc_config *config); 44 static void config_set(const char *dirpath, struct hammer_ioc_config *config); 45 static void config_remove_path(void); 46 47 char *ConfigPath; 48 49 /* 50 * hammer config [<fs> [configfile]] 51 * 52 * Prints out the hammer cleanup configuration for the specified HAMMER 53 * filesystem(s) or the current filesystem. 54 */ 55 void 56 hammer_cmd_config(char **av, int ac) 57 { 58 struct hammer_ioc_config config; 59 char *dirpath; 60 ssize_t n; 61 int fd; 62 63 bzero(&config, sizeof(config)); 64 if (ac == 0) { 65 config_get(".", &config); 66 if (config.head.error == 0) { 67 printf("%s", config.config.text); 68 } else { 69 errx(2, "hammer config: no configuration found"); 70 /* not reached */ 71 } 72 return; 73 } 74 dirpath = av[0]; 75 if (ac == 1) { 76 config_get(dirpath, &config); 77 if (config.head.error == 0) { 78 printf("%s", config.config.text); 79 } else { 80 errx(2, "hammer config: no configuration found"); 81 /* not reached */ 82 } 83 return; 84 } 85 config_get(dirpath, &config); /* ignore errors */ 86 config.head.error = 0; 87 88 fd = open(av[1], O_RDONLY); 89 if (fd < 0) { 90 err(2, "hammer config: %s", av[1]); 91 /* not reached */ 92 } 93 n = read(fd, config.config.text, sizeof(config.config.text) - 1); 94 if (n == sizeof(config.config.text) - 1) { 95 err(2, "hammer config: config file too big, limit %zu bytes", 96 sizeof(config.config.text) - 1); 97 /* not reached */ 98 } 99 bzero(config.config.text + n, sizeof(config.config.text) - n); 100 config_set(dirpath, &config); 101 close(fd); 102 } 103 104 /* 105 * hammer viconfig [<fs>] 106 */ 107 void 108 hammer_cmd_viconfig(char **av, int ac) 109 { 110 struct hammer_ioc_config config; 111 struct timeval times[2]; 112 const char *dirpath; 113 struct stat st; 114 char *runcmd, *editor, *tmp; 115 char path[32]; 116 ssize_t n; 117 int fd; 118 119 if (ac > 1) { 120 errx(1, "hammer viconfig: 0 or 1 argument (<fs>) only"); 121 /* not reached */ 122 } 123 if (ac == 0) 124 dirpath = "."; 125 else 126 dirpath = av[0]; 127 config_get(dirpath, &config); 128 if (config.head.error == ENOENT) { 129 snprintf(config.config.text, sizeof(config.config.text), 130 "%s", 131 "# No configuration present, here are some defaults\n" 132 "# you can uncomment. Also remove these instructions\n" 133 "#\n" 134 "#snapshots 1d 60d\n" 135 "#prune 1d 5m\n" 136 "#rebalance 1d 5m\n" 137 "#dedup 1d 5m\n" 138 "#reblock 1d 5m\n" 139 "#recopy 30d 10m\n"); 140 config.head.error = 0; 141 } 142 if (config.head.error) { 143 errx(2, "hammer viconfig: read config failed error: %s", 144 strerror(config.head.error)); 145 /* not reached */ 146 } 147 148 /* 149 * Edit a temporary file and write back if it was modified. 150 * Adjust the mtime back one second so a quick edit is not 151 * improperly detected as not having been modified. 152 */ 153 snprintf(path, sizeof(path), "/tmp/configXXXXXXXXXX"); 154 mkstemp(path); 155 ConfigPath = path; 156 atexit(config_remove_path); 157 158 fd = open(path, O_RDWR|O_CREAT|O_TRUNC, 0600); 159 if (fd < 0) 160 err(2, "hammer viconfig: creating temporary file %s", path); 161 write(fd, config.config.text, strlen(config.config.text)); 162 if (fstat(fd, &st) < 0) 163 err(2, "hammer viconfig"); 164 times[0].tv_sec = st.st_mtime - 1; 165 times[0].tv_usec = 0; 166 times[1] = times[0]; 167 close(fd); 168 utimes(path, times); 169 170 if ((tmp = getenv("EDITOR")) != NULL || 171 (tmp = getenv("VISUAL")) != NULL) 172 editor = strdup(tmp); 173 else 174 editor = strdup("vi"); 175 176 asprintf(&runcmd, "%s %s", editor, path); 177 system(runcmd); 178 179 if (stat(path, &st) < 0) 180 err(2, "hammer viconfig: unable to stat file after vi"); 181 if (times[0].tv_sec == st.st_mtime) { 182 printf("hammer viconfig: no changes were made\n"); 183 remove(path); 184 return; 185 } 186 fd = open(path, O_RDONLY); 187 if (fd < 0) 188 err(2, "hammer viconfig: unable to read %s", path); 189 remove(path); 190 n = read(fd, config.config.text, sizeof(config.config.text) - 1); 191 if (n < 0) 192 err(2, "hammer viconfig: unable to read %s", path); 193 if (n == sizeof(config.config.text) - 1) { 194 err(2, "hammer config: config file too big, limit %zu bytes", 195 sizeof(config.config.text) - 1); 196 /* not reached */ 197 } 198 bzero(config.config.text + n, sizeof(config.config.text) - n); 199 config_set(dirpath, &config); 200 free(editor); 201 free(runcmd); 202 } 203 204 static void 205 config_get(const char *dirpath, struct hammer_ioc_config *config) 206 { 207 struct hammer_ioc_version version; 208 int fd; 209 210 bzero(&version, sizeof(version)); 211 if ((fd = open(dirpath, O_RDONLY)) < 0) 212 err(2, "hammer config: unable to open directory %s", dirpath); 213 if (ioctl(fd, HAMMERIOC_GET_VERSION, &version) < 0) 214 errx(2, "hammer config: not a HAMMER filesystem!"); 215 216 if (ioctl(fd, HAMMERIOC_GET_CONFIG, config) < 0) 217 errx(2, "hammer config: config_get"); 218 close(fd); 219 } 220 221 static void 222 config_set(const char *dirpath, struct hammer_ioc_config *config) 223 { 224 struct hammer_ioc_version version; 225 int fd; 226 227 bzero(&version, sizeof(version)); 228 if ((fd = open(dirpath, O_RDONLY)) < 0) 229 errx(2, "hammer config: unable to open directory %s", dirpath); 230 if (ioctl(fd, HAMMERIOC_GET_VERSION, &version) < 0) 231 errx(2, "hammer config: not a HAMMER filesystem!"); 232 if (ioctl(fd, HAMMERIOC_SET_CONFIG, config) < 0) 233 err(2, "hammer config"); 234 close(fd); 235 } 236 237 static void 238 config_remove_path(void) 239 { 240 remove(ConfigPath); 241 } 242