Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 313 lines (276 sloc) 10.589 kb
98a5725 @hzeller o Start implementation. Just a basic forwarding filesystem for
authored
1 // Copyright (C) 2012 Henner Zeller <h.zeller@acm.org>
2 //
3 // This program is free software; you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation; either version 3 of the License, or
6 // (at your option) any later version.
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 // Use latest version.
17 #define FUSE_USE_VERSION 26
18 #include <fuse.h>
a9d3e53 @hzeller o Found a project name: "Folve". Some renamings because of that.
authored
19
98a5725 @hzeller o Start implementation. Just a basic forwarding filesystem for
authored
20 #include <dirent.h>
21 #include <errno.h>
b698853 @hzeller o README updates.
authored
22 #include <fcntl.h>
98a5725 @hzeller o Start implementation. Just a basic forwarding filesystem for
authored
23 #include <limits.h>
b698853 @hzeller o README updates.
authored
24 #include <stdio.h>
98a5725 @hzeller o Start implementation. Just a basic forwarding filesystem for
authored
25 #include <stdlib.h>
b698853 @hzeller o README updates.
authored
26 #include <string.h>
27 #include <sys/stat.h>
28 #include <sys/time.h>
49eca7f @hzeller o add syslog as preparation for parameter handling.
authored
29 #include <syslog.h>
b698853 @hzeller o README updates.
authored
30 #include <unistd.h>
98a5725 @hzeller o Start implementation. Just a basic forwarding filesystem for
authored
31
a9d3e53 @hzeller o Found a project name: "Folve". Some renamings because of that.
authored
32 #include "folve-filesystem.h"
8d1fbe7 @hzeller o Move fuse-convolve.c to fuse-convolve.cc. Make filter-interface
authored
33 #include "status-server.h"
7689c9a @hzeller o preparation to extract classes from folve-filesystem into their
authored
34 #include "util.h"
e9c07dd @hzeller o add documentation
authored
35
6c6dedb @hzeller o Some readme tweaks and readability improvements.
authored
36 // Compilation unit variables to communicate with the fuse callbacks.
27c44cd @hzeller o Use fuse option handling to extract our flags and options.
authored
37 static struct FolveRuntime {
a372a03 @hzeller o Make meta-refresh configurable.
authored
38 FolveRuntime() : fs(NULL), mount_point(NULL),
436f57d @hzeller o Have a default refresh time of 10 seconds.
authored
39 status_port(-1), refresh_time(10) {}
0330f76 @hzeller o Only start status server after we're daemonized, otherwise
authored
40 FolveFilesystem *fs;
27c44cd @hzeller o Use fuse option handling to extract our flags and options.
authored
41 const char *mount_point;
0330f76 @hzeller o Only start status server after we're daemonized, otherwise
authored
42 int status_port;
a372a03 @hzeller o Make meta-refresh configurable.
authored
43 int refresh_time;
ee675fe @hzeller o Provide a way to select between different filter diretories.
authored
44 } folve_rt;
98a5725 @hzeller o Start implementation. Just a basic forwarding filesystem for
authored
45
7c7bda6 @hzeller o attempt to create a sndfile for every file, if that fails, fall
authored
46 static char *concat_path(char *buf, const char *a, const char *b) {
47 strcpy(buf, a);
48 strcat(buf, b);
49 return buf;
50 }
51
98a5725 @hzeller o Start implementation. Just a basic forwarding filesystem for
authored
52 // Given a relative path from the root of the mounted file-system, get the
53 // original file from the source filesystem.
54 static const char *assemble_orig_path(char *buf, const char *path) {
08bbf28 @hzeller o First shot at gapless. Works pretty well for my test-case.
authored
55 return concat_path(buf, folve_rt.fs->underlying_dir().c_str(), path);
98a5725 @hzeller o Start implementation. Just a basic forwarding filesystem for
authored
56 }
57
7c7bda6 @hzeller o attempt to create a sndfile for every file, if that fails, fall
authored
58 // Essentially lstat(). Just forward to the original filesystem (this
73093dc @hzeller o Keep open file-descriptor.
authored
59 // will by lying: our convolved files are of different size...)
0330f76 @hzeller o Only start status server after we're daemonized, otherwise
authored
60 static int folve_getattr(const char *path, struct stat *stbuf) {
9a8ea56 @hzeller o add infrastructure to provide Stat() output of currently open
authored
61 // If this is a currently open filename, we might be able to output a better
62 // estimate.
ee675fe @hzeller o Provide a way to select between different filter diretories.
authored
63 int result = folve_rt.fs->StatByFilename(path, stbuf);
369a0a7 @hzeller o Writing is not possible. Show permissions as readonly.
authored
64 if (result != 0) {
65 char path_buf[PATH_MAX];
66 result = lstat(assemble_orig_path(path_buf, path), stbuf);
67 if (result == -1)
68 return -errno;
69 }
70 // Whatever write mode was there before: now things are readonly.
71 stbuf->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
98a5725 @hzeller o Start implementation. Just a basic forwarding filesystem for
authored
72 return 0;
73 }
74
73093dc @hzeller o Keep open file-descriptor.
authored
75 // readdir(). Just forward to original filesystem.
0330f76 @hzeller o Only start status server after we're daemonized, otherwise
authored
76 static int folve_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
77 off_t offset, struct fuse_file_info *fi) {
98a5725 @hzeller o Start implementation. Just a basic forwarding filesystem for
authored
78 DIR *dp;
79 struct dirent *de;
80 char path_buf[PATH_MAX];
81
82 dp = opendir(assemble_orig_path(path_buf, path));
83 if (dp == NULL)
84 return -errno;
85
86 while ((de = readdir(dp)) != NULL) {
87 struct stat st;
88 memset(&st, 0, sizeof(st));
89 st.st_ino = de->d_ino;
90 st.st_mode = de->d_type << 12;
7c7bda6 @hzeller o attempt to create a sndfile for every file, if that fails, fall
authored
91 const char *entry_name = de->d_name;
92 if (filler(buf, entry_name, &st, 0))
98a5725 @hzeller o Start implementation. Just a basic forwarding filesystem for
authored
93 break;
94 }
95
96 closedir(dp);
97 return 0;
98 }
99
73093dc @hzeller o Keep open file-descriptor.
authored
100 // readlink(): forward to original filesystem.
0330f76 @hzeller o Only start status server after we're daemonized, otherwise
authored
101 static int folve_readlink(const char *path, char *buf, size_t size) {
98a5725 @hzeller o Start implementation. Just a basic forwarding filesystem for
authored
102 char path_buf[PATH_MAX];
103 const int result = readlink(assemble_orig_path(path_buf, path),
104 buf, size - 1);
105 if (result == -1)
106 return -errno;
107
108 buf[result] = '\0';
109 return 0;
110 }
111
0330f76 @hzeller o Only start status server after we're daemonized, otherwise
authored
112 static int folve_open(const char *path, struct fuse_file_info *fi) {
8b18938 @hzeller o some comment changes. Ready to do some flac handling.
authored
113 // We want to be allowed to only return part of the requested data in read().
114 // That way, we can separate reading the ID3-tags from
115 // decoding of the music stream - that way indexing should be fast.
116 // Setting the flag 'direct_io' allows us to return partial results.
117 fi->direct_io = 1;
118
e9c07dd @hzeller o add documentation
authored
119 // The file-handle has the neat property to be 64 bit - so we can actually
b698853 @hzeller o README updates.
authored
120 // stuff a pointer to our file handler object in there :)
8b18938 @hzeller o some comment changes. Ready to do some flac handling.
authored
121 // (Yay, someone was thinking while developing that API).
08bbf28 @hzeller o First shot at gapless. Works pretty well for my test-case.
authored
122 FileHandler *handler = folve_rt.fs->GetOrCreateHandler(path);
680f590 @hzeller o report opening error.
authored
123 if (handler == NULL)
124 return -errno;
125 fi->fh = (uint64_t) handler;
126 return 0;
98a5725 @hzeller o Start implementation. Just a basic forwarding filesystem for
authored
127 }
128
0330f76 @hzeller o Only start status server after we're daemonized, otherwise
authored
129 static int folve_read(const char *path, char *buf, size_t size, off_t offset,
130 struct fuse_file_info *fi) {
8d1fbe7 @hzeller o Move fuse-convolve.c to fuse-convolve.cc. Make filter-interface
authored
131 return reinterpret_cast<FileHandler *>(fi->fh)->Read(buf, size, offset);
98a5725 @hzeller o Start implementation. Just a basic forwarding filesystem for
authored
132 }
133
0330f76 @hzeller o Only start status server after we're daemonized, otherwise
authored
134 static int folve_release(const char *path, struct fuse_file_info *fi) {
ee675fe @hzeller o Provide a way to select between different filter diretories.
authored
135 folve_rt.fs->Close(path, reinterpret_cast<FileHandler *>(fi->fh));
8d1fbe7 @hzeller o Move fuse-convolve.c to fuse-convolve.cc. Make filter-interface
authored
136 return 0;
9a8ea56 @hzeller o add infrastructure to provide Stat() output of currently open
authored
137 }
138
0330f76 @hzeller o Only start status server after we're daemonized, otherwise
authored
139 static int folve_fgetattr(const char *path, struct stat *result,
140 struct fuse_file_info *fi) {
8d1fbe7 @hzeller o Move fuse-convolve.c to fuse-convolve.cc. Make filter-interface
authored
141 return reinterpret_cast<FileHandler *>(fi->fh)->Stat(result);
73093dc @hzeller o Keep open file-descriptor.
authored
142 }
98a5725 @hzeller o Start implementation. Just a basic forwarding filesystem for
authored
143
0330f76 @hzeller o Only start status server after we're daemonized, otherwise
authored
144 static void *folve_init(struct fuse_conn_info *conn) {
145 const int ident_len = 20;
146 char *ident = (char*) malloc(ident_len); // openlog() keeps reference. Leaks.
27c44cd @hzeller o Use fuse option handling to extract our flags and options.
authored
147 snprintf(ident, ident_len, "folve[%d]", getpid());
0330f76 @hzeller o Only start status server after we're daemonized, otherwise
authored
148 openlog(ident, LOG_CONS|LOG_PERROR, LOG_USER);
ee675fe @hzeller o Provide a way to select between different filter diretories.
authored
149 syslog(LOG_INFO, "Version " FOLVE_VERSION " started. "
150 "Serving '%s' on mount point '%s'",
151 folve_rt.fs->underlying_dir().c_str(), folve_rt.mount_point);
7689c9a @hzeller o preparation to extract classes from folve-filesystem into their
authored
152 if (folve::IsDebugLogEnabled()) {
5830c5a @hzeller o Switch logging to syslog()
authored
153 syslog(LOG_INFO, "Debug logging enabled (-D)");
154 }
0330f76 @hzeller o Only start status server after we're daemonized, otherwise
authored
155
ee675fe @hzeller o Provide a way to select between different filter diretories.
authored
156 if (folve_rt.status_port > 0) {
0330f76 @hzeller o Only start status server after we're daemonized, otherwise
authored
157 // Need to start status server after we're daemonized.
ee675fe @hzeller o Provide a way to select between different filter diretories.
authored
158 StatusServer *status_server = new StatusServer(folve_rt.fs);
159 if (status_server->Start(folve_rt.status_port)) {
a372a03 @hzeller o Make meta-refresh configurable.
authored
160 syslog(LOG_INFO, "HTTP status server on port %d; refresh=%d",
161 folve_rt.status_port, folve_rt.refresh_time);
162 status_server->set_meta_refresh(folve_rt.refresh_time);
0330f76 @hzeller o Only start status server after we're daemonized, otherwise
authored
163 } else {
164 syslog(LOG_ERR, "Couldn't start HTTP server on port %d\n",
ee675fe @hzeller o Provide a way to select between different filter diretories.
authored
165 folve_rt.status_port);
0330f76 @hzeller o Only start status server after we're daemonized, otherwise
authored
166 }
167 }
ee675fe @hzeller o Provide a way to select between different filter diretories.
authored
168
169 // Some sanity checks.
170 if (folve_rt.fs->config_dirs().size() == 1) {
171 syslog(LOG_NOTICE, "No filter configuration directories given. "
172 "Any files will be just passed through verbatim.");
173 }
174 if (folve_rt.fs->config_dirs().size() > 2 && folve_rt.status_port < 0) {
175 syslog(LOG_WARNING, "Multiple filter configurations given, but no HTTP "
176 "status port. You only can switch filters via the HTTP interface; "
177 "add -p <port>");
178 }
0330f76 @hzeller o Only start status server after we're daemonized, otherwise
authored
179 return NULL;
180 }
98a5725 @hzeller o Start implementation. Just a basic forwarding filesystem for
authored
181
0a3400d @hzeller o Write when we're exiting.
authored
182 static void folve_destroy(void *) {
db66f7d @hzeller o print which directory is serviced.
authored
183 syslog(LOG_INFO, "Exiting.");
0a3400d @hzeller o Write when we're exiting.
authored
184 }
185
b698853 @hzeller o README updates.
authored
186 static int usage(const char *prg) {
5830c5a @hzeller o Switch logging to syslog()
authored
187 printf("usage: %s [options] <original-dir> <mount-point>\n", prg);
188 printf("Options: (in sequence of usefulness)\n"
062a435 @hzeller o change configuration directory wording.
authored
189 "\t-c <cfg-dir> : Convolver configuration directory.\n"
8c7e5c1 @hzeller o Only if -D is given, the debug toggle is active in the UI.
authored
190 "\t You can supply this option multiple times:\n"
a1ebba2 @hzeller o select box is replaced by radio buttons.
authored
191 "\t Select with radio-buttons on the HTTP status page.\n"
5830c5a @hzeller o Switch logging to syslog()
authored
192 "\t-p <port> : Port to run the HTTP status server on.\n"
436f57d @hzeller o Have a default refresh time of 10 seconds.
authored
193 "\t-r <refresh> : Seconds between refresh of status page;\n"
194 "\t Default is %d seconds; switch off with -1.\n"
f9b9bc9 @hzeller o gapless not experimental anymore.
authored
195 "\t-g : Gapless convolving alphabetically adjacent files.\n"
8c7e5c1 @hzeller o Only if -D is given, the debug toggle is active in the UI.
authored
196 "\t-D : Moderate volume Folve debug messages to syslog.\n"
197 "\t Can then also be toggled in the UI.\n"
5830c5a @hzeller o Switch logging to syslog()
authored
198 "\t-f : Operate in foreground; useful for debugging.\n"
199 "\t-o <mnt-opt> : other generic mount parameters passed to fuse.\n"
436f57d @hzeller o Have a default refresh time of 10 seconds.
authored
200 "\t-d : High volume fuse debug log. Implies -f.\n",
201 folve_rt.refresh_time);
8b18938 @hzeller o some comment changes. Ready to do some flac handling.
authored
202 return 1;
98a5725 @hzeller o Start implementation. Just a basic forwarding filesystem for
authored
203 }
204
27c44cd @hzeller o Use fuse option handling to extract our flags and options.
authored
205 struct FolveConfig {
206 FolveConfig() : base_dir(NULL), config_dir(NULL), port(-1) {}
207 const char *base_dir;
208 const char *mount_point;
209 const char *config_dir;
210 int port;
211 };
212
213 enum {
214 FOLVE_OPT_PORT = 42,
a372a03 @hzeller o Make meta-refresh configurable.
authored
215 FOLVE_OPT_REFRESH_TIME,
27c44cd @hzeller o Use fuse option handling to extract our flags and options.
authored
216 FOLVE_OPT_CONFIG,
5830c5a @hzeller o Switch logging to syslog()
authored
217 FOLVE_OPT_DEBUG,
08bbf28 @hzeller o First shot at gapless. Works pretty well for my test-case.
authored
218 FOLVE_OPT_GAPLESS,
27c44cd @hzeller o Use fuse option handling to extract our flags and options.
authored
219 };
220
221 int FolveOptionHandling(void *data, const char *arg, int key,
222 struct fuse_args *outargs) {
df18f63 @hzeller o Make path-names to filters and base-directory absolute
authored
223 char realpath_buf[PATH_MAX]; // running as daemon, need absolute names.
ee675fe @hzeller o Provide a way to select between different filter diretories.
authored
224 FolveRuntime *rt = (FolveRuntime*) data;
27c44cd @hzeller o Use fuse option handling to extract our flags and options.
authored
225 switch (key) {
226 case FUSE_OPT_KEY_NONOPT:
ee675fe @hzeller o Provide a way to select between different filter diretories.
authored
227 // First non-opt: our underlying dir.
228 if (rt->fs->underlying_dir().empty()) {
cf1b0e0 @hzeller o README demo tweaking
authored
229 const char *base_dir = realpath(arg, realpath_buf);
230 if (base_dir != NULL) {
231 rt->fs->set_underlying_dir(base_dir);
232 } else {
233 fprintf(stderr, "Invalid base path '%s': %s\n",
234 arg, strerror(errno));
235 }
ee675fe @hzeller o Provide a way to select between different filter diretories.
authored
236 return 0; // we consumed this.
27c44cd @hzeller o Use fuse option handling to extract our flags and options.
authored
237 } else {
ee675fe @hzeller o Provide a way to select between different filter diretories.
authored
238 rt->mount_point = strdup(arg); // remmber as FYI
239 return 1; // .. but leave it to fuse
27c44cd @hzeller o Use fuse option handling to extract our flags and options.
authored
240 }
241 case FOLVE_OPT_PORT:
ee675fe @hzeller o Provide a way to select between different filter diretories.
authored
242 rt->status_port = atoi(arg + 2); // strip "-p"
27c44cd @hzeller o Use fuse option handling to extract our flags and options.
authored
243 return 0;
a372a03 @hzeller o Make meta-refresh configurable.
authored
244 case FOLVE_OPT_REFRESH_TIME:
245 rt->refresh_time = atoi(arg + 2); // strip "-r"
246 return 0;
cf1b0e0 @hzeller o README demo tweaking
authored
247 case FOLVE_OPT_CONFIG: {
248 const char *config_dir = realpath(arg + 2, realpath_buf); // strip "-c"
249 if (config_dir != NULL) {
250 rt->fs->add_config_dir(config_dir);
251 } else {
252 fprintf(stderr, "Invalid config dir '%s': %s\n",
253 arg + 2, strerror(errno));
254 }
27c44cd @hzeller o Use fuse option handling to extract our flags and options.
authored
255 return 0;
cf1b0e0 @hzeller o README demo tweaking
authored
256 }
5830c5a @hzeller o Switch logging to syslog()
authored
257 case FOLVE_OPT_DEBUG:
8c7e5c1 @hzeller o Only if -D is given, the debug toggle is active in the UI.
authored
258 rt->fs->set_debug_ui_enabled(true);
7689c9a @hzeller o preparation to extract classes from folve-filesystem into their
authored
259 folve::EnableDebugLog(true);
5830c5a @hzeller o Switch logging to syslog()
authored
260 return 0;
08bbf28 @hzeller o First shot at gapless. Works pretty well for my test-case.
authored
261 case FOLVE_OPT_GAPLESS:
262 rt->fs->set_gapless_processing(true);
263 return 0;
27c44cd @hzeller o Use fuse option handling to extract our flags and options.
authored
264 }
265 return 1;
266 }
267
98a5725 @hzeller o Start implementation. Just a basic forwarding filesystem for
authored
268 int main(int argc, char *argv[]) {
1e9ff21 @hzeller o main: report if parameters are not directories.
authored
269 const char *progname = argv[0];
8b18938 @hzeller o some comment changes. Ready to do some flac handling.
authored
270 if (argc < 4) {
1e9ff21 @hzeller o main: report if parameters are not directories.
authored
271 return usage(progname);
98a5725 @hzeller o Start implementation. Just a basic forwarding filesystem for
authored
272 }
49eca7f @hzeller o add syslog as preparation for parameter handling.
authored
273
ee675fe @hzeller o Provide a way to select between different filter diretories.
authored
274 folve_rt.fs = new FolveFilesystem();
275
27c44cd @hzeller o Use fuse option handling to extract our flags and options.
authored
276 static struct fuse_opt folve_options[] = {
5830c5a @hzeller o Switch logging to syslog()
authored
277 FUSE_OPT_KEY("-p ", FOLVE_OPT_PORT),
a372a03 @hzeller o Make meta-refresh configurable.
authored
278 FUSE_OPT_KEY("-r ", FOLVE_OPT_REFRESH_TIME),
5830c5a @hzeller o Switch logging to syslog()
authored
279 FUSE_OPT_KEY("-c ", FOLVE_OPT_CONFIG),
280 FUSE_OPT_KEY("-D", FOLVE_OPT_DEBUG),
08bbf28 @hzeller o First shot at gapless. Works pretty well for my test-case.
authored
281 FUSE_OPT_KEY("-g", FOLVE_OPT_GAPLESS),
27c44cd @hzeller o Use fuse option handling to extract our flags and options.
authored
282 FUSE_OPT_END
283 };
284 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
ee675fe @hzeller o Provide a way to select between different filter diretories.
authored
285 fuse_opt_parse(&args, &folve_rt, folve_options, FolveOptionHandling);
27c44cd @hzeller o Use fuse option handling to extract our flags and options.
authored
286
ee675fe @hzeller o Provide a way to select between different filter diretories.
authored
287 if (!folve_rt.fs->CheckInitialized()) {
1e9ff21 @hzeller o main: report if parameters are not directories.
authored
288 return usage(progname);
289 }
0330f76 @hzeller o Only start status server after we're daemonized, otherwise
authored
290
291 struct fuse_operations folve_operations;
292 memset(&folve_operations, 0, sizeof(folve_operations));
8d1fbe7 @hzeller o Move fuse-convolve.c to fuse-convolve.cc. Make filter-interface
authored
293
0a3400d @hzeller o Write when we're exiting.
authored
294 // Start/stop. Will write to syslog and start auxiliary http service.
0330f76 @hzeller o Only start status server after we're daemonized, otherwise
authored
295 folve_operations.init = folve_init;
0a3400d @hzeller o Write when we're exiting.
authored
296 folve_operations.destroy = folve_destroy;
8d1fbe7 @hzeller o Move fuse-convolve.c to fuse-convolve.cc. Make filter-interface
authored
297
298 // Basic operations to make navigation work.
0330f76 @hzeller o Only start status server after we're daemonized, otherwise
authored
299 folve_operations.readdir = folve_readdir;
300 folve_operations.readlink = folve_readlink;
8d1fbe7 @hzeller o Move fuse-convolve.c to fuse-convolve.cc. Make filter-interface
authored
301
302 // open() and close() file.
38cdeb1 @hzeller o whitespace changes.
authored
303 folve_operations.open = folve_open;
0330f76 @hzeller o Only start status server after we're daemonized, otherwise
authored
304 folve_operations.release = folve_release;
8d1fbe7 @hzeller o Move fuse-convolve.c to fuse-convolve.cc. Make filter-interface
authored
305
306 // Actual workhorse: reading a file and returning predicted file-size
0330f76 @hzeller o Only start status server after we're daemonized, otherwise
authored
307 folve_operations.read = folve_read;
308 folve_operations.fgetattr = folve_fgetattr;
309 folve_operations.getattr = folve_getattr;
98a5725 @hzeller o Start implementation. Just a basic forwarding filesystem for
authored
310
27c44cd @hzeller o Use fuse option handling to extract our flags and options.
authored
311 return fuse_main(args.argc, args.argv, &folve_operations, NULL);
98a5725 @hzeller o Start implementation. Just a basic forwarding filesystem for
authored
312 }
Something went wrong with that request. Please try again.