mirror of
				https://github.com/owntone/owntone-server.git
				synced 2025-10-30 00:05:05 -04:00 
			
		
		
		
	autocreate directories from config file
This commit is contained in:
		
							parent
							
								
									e1a1c7f18a
								
							
						
					
					
						commit
						abee2d9792
					
				| @ -103,7 +103,7 @@ fi | ||||
| 
 | ||||
| dnl Darwin's stupid cpp preprocessor.... | ||||
| echo Host type is $host | ||||
| CPPFLAGS="$CPPFLAGS -DHOST='\"$host\"' -DPREFIX=$prefix" | ||||
| CPPFLAGS="$CPPFLAGS -DHOST='\"$host\"'" | ||||
| 
 | ||||
| dnl | ||||
| dnl The apple mDNS stuff wants these compile flags. | ||||
|  | ||||
| @ -1,307 +0,0 @@ | ||||
| # $Id$ | ||||
| # | ||||
| # This is the mt-daapd config file. | ||||
| # | ||||
| # If you have problems or questions with the format of this file, | ||||
| # direct your questions to rpedde@users.sourceforge.net. | ||||
| # | ||||
| # You can also check the website at http://mt-daapd.sourceforge.net, | ||||
| # as there is a growing documentation library there, peer-supported | ||||
| # forums and possibly more. | ||||
| # | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # web_root (required) | ||||
| # | ||||
| # Location of the admin web pages. | ||||
| # | ||||
| # If you installed from .RPM, .deb, or tarball with --prefix=/usr, then | ||||
| # this is correct.  | ||||
| # | ||||
| # If you installed from tarball without --prefix=/usr, then the correct | ||||
| # path is probably /usr/local/share/mt-daapd/admin-root. | ||||
| # | ||||
| 
 | ||||
| web_root	/usr/share/mt-daapd/admin-root | ||||
| 
 | ||||
| # | ||||
| # port (required) | ||||
| # | ||||
| # What port to listen on.  It is possible to use a different | ||||
| # port, but this is the default iTunes port | ||||
| # | ||||
| 
 | ||||
| port		3689 | ||||
| 
 | ||||
| # | ||||
| # admin_pw (required) | ||||
| # | ||||
| # This is the password to the administrative pages | ||||
| # | ||||
| 
 | ||||
| admin_pw	mt-daapd | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # db_type (required) | ||||
| # | ||||
| # This is what kind of backend database to store the song | ||||
| # info in.  Valid choices are "sqlite" and "sqlite3". | ||||
| # | ||||
| 
 | ||||
| db_type sqlite | ||||
| 
 | ||||
| # | ||||
| # db_parms | ||||
| # | ||||
| # This is any extra information the db needs to connect. | ||||
| # in the case of sqlite and sqlite3, this is the name | ||||
| # of the directory to store the database in | ||||
| # | ||||
| # If you installed from RPM or .deb, this path likely already | ||||
| # exists.  If not, then you must create it.  The directory itself | ||||
| # must be writable by the "runas" user. | ||||
| # | ||||
| 
 | ||||
| db_parms  /var/cache/mt-daapd | ||||
| 
 | ||||
| # | ||||
| # mp3_dir (required) | ||||
| # | ||||
| # Location of the mp3 files to share.  Note that because the | ||||
| # files are stored in the database by inode, these must be | ||||
| # in the same physical filesystem. | ||||
| # | ||||
| 
 | ||||
| mp3_dir		/mnt/mp3 | ||||
| 
 | ||||
| # | ||||
| # servername (required) | ||||
| # | ||||
| # This is both the name of the server as advertised | ||||
| # via rendezvous, and the name of the database | ||||
| # exported via DAAP.  Also know as "What shows up in iTunes". | ||||
| # | ||||
| 
 | ||||
| servername	mt-daapd | ||||
| 
 | ||||
| # | ||||
| # runas (required) | ||||
| # | ||||
| # This is the user to drop privs to if running as | ||||
| # root.  If mt-daapd is not started as root, this | ||||
| # configuration option is ignored.  Notice that this | ||||
| # must be specified whether the server is running | ||||
| # as root or not. | ||||
| # | ||||
| 
 | ||||
| runas	nobody | ||||
| 
 | ||||
| # | ||||
| # playlist (optional) | ||||
| # | ||||
| # This is the location of a playlist file. | ||||
| # This is for Apple-style "Smart Playlists" | ||||
| # See the mt-daapd.playlist file in the  | ||||
| # contrib directory for syntax and examples | ||||
| # | ||||
| # This doesn't control static playlists... these | ||||
| # are controlled with the "process_m3u" directive | ||||
| # below. | ||||
| # | ||||
| 
 | ||||
| playlist	/etc/mt-daapd.playlist | ||||
| 
 | ||||
| # | ||||
| # password (optional) | ||||
| # | ||||
| # This is the password required to listen to MP3 files | ||||
| # i.e. the password that iTunes prompts for | ||||
| # | ||||
| 
 | ||||
| #password	mp3 | ||||
| 
 | ||||
| # | ||||
| # extensions (optional) | ||||
| # | ||||
| # These are the file extensions that the daap server will | ||||
| # try to index and serve.  By default, it only indexes and | ||||
| # serves .mp3 files.  It can also server .m4a and .m4p files, | ||||
| # and just about any other files, really.  Unfortunately, while | ||||
| # it can *attempt* to serve other files (.ogg?), iTunes won't | ||||
| # play them.  Perhaps this would be useful on Linux with | ||||
| # Rhythmbox, once it understands daap.  (hurry up!) | ||||
| # | ||||
| # Failing that, one can use server-side conversion to transcode | ||||
| # non-standard (.ogg, .flac) music to wav on the server side. | ||||
| # See the ssc_* options below. | ||||
| # | ||||
| 
 | ||||
| extensions .mp3,.m4a,.m4p,.ogg | ||||
| 
 | ||||
| # | ||||
| # ssc_codectypes (optional) | ||||
| # | ||||
| # List of codectypes for files that the daap server should | ||||
| # perform internal format conversion and present to clients | ||||
| # as WAV files.  The file extensions that these codectypes correspond | ||||
| # to must also be present in 'extensions' | ||||
| # configuration value, or files are not probed in the first | ||||
| # place. | ||||
| # | ||||
| # Valid codectypes: | ||||
| # | ||||
| # mp4a - for AAC (.aac, .mp4, .m4a, .m4p) | ||||
| # mpeg - for mp3 | ||||
| # wav - for wav | ||||
| # wma - for wma | ||||
| # ogg - for ogg | ||||
| # flac - for flac (.flac, .fla) | ||||
| # mpc for musepack (.mpc, .mpp, .mp+) | ||||
| # alac for alac (.m4a) | ||||
| # | ||||
| 
 | ||||
| ssc_codectypes ogg,flac,alac | ||||
| 
 | ||||
| # | ||||
| # ssc_prog (optional) | ||||
| # | ||||
| # Program that is used in server side format conversion. | ||||
| # Program must accept following command line syntax: | ||||
| #     ssc_prog filename offset length ... | ||||
| # Parameter filename is the real name of the file that is | ||||
| # to be converted and streamed, offset is number of bytes | ||||
| # that are skipped from the beginning of the _output_ file | ||||
| # before streaming is started, length is length of the song | ||||
| # in seconds (or zero).  All other possible arguments must | ||||
| # be ignored.  The resulting wav file (or the rest of | ||||
| # the file after initial seek) is written to the standard | ||||
| # output by the ssc_prog program.  This is typically | ||||
| # a script that is a front end for different conversion tools | ||||
| # handling different formats. | ||||
| # | ||||
| 
 | ||||
| ssc_prog /etc/mt-daapd-ssc-script | ||||
| 
 | ||||
| # | ||||
| # logfile (optional) | ||||
| # | ||||
| # This is the file to log to.  If this is not configured, | ||||
| # then it will log to the syslog. | ||||
| # | ||||
| # Not that the -d <level> switch will control the log verbosity. | ||||
| # By default, it runs at log level 1.  Log level 9 will churn | ||||
| # out scads of useless debugging information.  Values in between | ||||
| # will vary the amount of logging you get. | ||||
| # | ||||
| 
 | ||||
| #logfile /var/log/mt-daapd.log | ||||
| 
 | ||||
| # | ||||
| # art_filename (optional) | ||||
| # | ||||
| # There is experimental support thanks to Hiren Joshi | ||||
| # (hirenj@mooh.org) for dynamically adding art to the id3v2 | ||||
| # header as it is streamed (!!).  If you were using a music system | ||||
| # like zina or andromeda, for example, with cover art called  | ||||
| # "_folderOpenImage.jpg", you could use the parameter | ||||
| # art_file _folderOpenImage.jpg and if the file _folderOpenImage.jpg | ||||
| # was located in the same folder as the .mp3 file, it would appear | ||||
| # in iTunes.  Cool, eh?  | ||||
| # | ||||
| 
 | ||||
| #art_filename _folderOpenImage.jpg | ||||
| 
 | ||||
| # | ||||
| # rescan_interval | ||||
| # | ||||
| # How often to check the file system to see if any mp3 files | ||||
| # have been added or removed.  | ||||
| # | ||||
| # if not specified, the default is 0, which disables background scanning. | ||||
| # | ||||
| # If background rescanning is disabled, a scan can still be forced from the  | ||||
| # "status" page of the administrative web interface | ||||
| # | ||||
| # Setting a rescan_interval lower than the time it takes to rescan | ||||
| # won't hurt anything, it will just waste CPU, and make connect times | ||||
| # to the daap server longer. | ||||
| # | ||||
| # | ||||
| 
 | ||||
| #rescan_interval 300 | ||||
| 
 | ||||
| # always_scan  | ||||
| # | ||||
| # The default behavior is not not do background rescans of the | ||||
| # filesystem unless there are clients connected.  The thought is to | ||||
| # allow the drives to spin down unless they are in use.  This might be | ||||
| # of more importance in IDE drives that aren't designed to be run | ||||
| # 24x7.  Forcing a scan through the web interface will always work | ||||
| # though, even if no users are connected. | ||||
| 
 | ||||
| # always_scan 0 | ||||
| 
 | ||||
| #  | ||||
| # process_m3u | ||||
| # | ||||
| # By default m3u processing is turned off, since most m3u files | ||||
| # sitting around in peoples mp3 directories have bad paths, and | ||||
| # I hear about it.  :) | ||||
| # | ||||
| # If you are sure your m3u files have good paths (i.e. unixly pathed, | ||||
| # with relative paths relative to the directory the m3u is in), then | ||||
| # you can turn on m3u processing by setting this directive to 1. | ||||
| # | ||||
| # I'm not sure "unixly" is a word, but you get the idea. | ||||
| # | ||||
| 
 | ||||
| # process_m3u 0 | ||||
| 
 | ||||
| # | ||||
| # scan_type | ||||
| #  | ||||
| # | ||||
| # This sets how aggressively mp3 files should be scanned to determine | ||||
| # file length.  There are three values: | ||||
| # | ||||
| # 0 (Normal)  | ||||
| #   Just scan the first mp3 frame to try and calculate size.  This will | ||||
| #   be accurate for most files, but VBR files without an Xing tag will | ||||
| #   probably have wildly inaccurate file times.  This is the default. | ||||
| # | ||||
| # 1 (Aggressive) | ||||
| #   This checks the bitrates of 10 frames in the middle of the song.   | ||||
| #   This will still be inaccurate for VBR files without an Xing tag, | ||||
| #   but they probably won't be quite as inaccurate as 0.  This takes | ||||
| #   more time, obviously, although the time hit will only happen the | ||||
| #   first time you scan a particular file. | ||||
| # | ||||
| # 2 (Painfully aggressive) | ||||
| #   This walks through the entire song, counting the number of frames. | ||||
| #   This should result in accurate song times, but will take the most | ||||
| #   time.  Again, this will only have to be incurred the first time | ||||
| #   the file is indexed. | ||||
| #  | ||||
| 
 | ||||
| # scan_type  0 | ||||
| 
 | ||||
| # | ||||
| # compress | ||||
| # | ||||
| # Whether to use gzip content-encoding when transferring playlists etc. | ||||
| # This was contributed as a patch by Ciamac Moallemi just prior to the 0.2.1 | ||||
| # release, and as such, hasn't gotten as much testing as other features. | ||||
| # | ||||
| # This feature should substantially speed up transfers of large databases | ||||
| # and playlists. | ||||
| # | ||||
| # It will eventually default to 1, but currently it defaults to 0. | ||||
| # | ||||
| 
 | ||||
| # compress 0 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										82
									
								
								src/conf.c
									
									
									
									
									
								
							
							
						
						
									
										82
									
								
								src/conf.c
									
									
									
									
									
								
							| @ -47,6 +47,7 @@ | ||||
| #include "err.h" | ||||
| #include "ll.h" | ||||
| #include "daapd.h" | ||||
| #include "os.h" | ||||
| 
 | ||||
| /** Globals */ | ||||
| //static int ecode;
 | ||||
| @ -79,7 +80,7 @@ static void _conf_lock(void); | ||||
| static void _conf_unlock(void); | ||||
| static int _conf_write(FILE *fp, LL *pll, int sublevel, char *parent); | ||||
| static CONF_ELEMENTS *_conf_get_keyinfo(char *section, char *key); | ||||
| static int _conf_makedir(char *path); | ||||
| static int _conf_makedir(char *path, char *user); | ||||
| static int _conf_existdir(char *path); | ||||
| 
 | ||||
| static CONF_ELEMENTS conf_elements[] = { | ||||
| @ -90,7 +91,7 @@ static CONF_ELEMENTS conf_elements[] = { | ||||
|     { 1, 0, CONF_T_STRING,"general","mp3_dir" }, | ||||
|     { 0, 1, CONF_T_EXISTPATH,"general","db_dir" }, | ||||
|     { 0, 0, CONF_T_STRING,"general","db_type" }, | ||||
|     { 0, 0, CONF_T_STRING,"general","db_parms" }, | ||||
|     { 0, 0, CONF_T_EXISTPATH,"general","db_parms" }, /* this isn't right */ | ||||
|     { 0, 0, CONF_T_INT,"general","debuglevel" }, | ||||
|     { 1, 0, CONF_T_STRING,"general","servername" }, | ||||
|     { 0, 0, CONF_T_INT,"general","rescan_interval" }, | ||||
| @ -119,11 +120,10 @@ static CONF_ELEMENTS conf_elements[] = { | ||||
|  * @returns TRUE on success, FALSE otherwise | ||||
|  */ | ||||
| 
 | ||||
| int _conf_makedir(char *path) { | ||||
| int _conf_makedir(char *path,char *user) { | ||||
|     char *token, *next_token; | ||||
|     char *pathdup; | ||||
|     char path_buffer[PATH_MAX]; | ||||
|     int err; | ||||
|     int retval = FALSE; | ||||
| 
 | ||||
|     DPRINTF(E_DBG,L_CONF,"Creating %s\n",path); | ||||
| @ -140,19 +140,20 @@ int _conf_makedir(char *path) { | ||||
|         if((strlen(path_buffer) + strlen(token)) < PATH_MAX) { | ||||
|             strcat(path_buffer,"/"); | ||||
|             strcat(path_buffer,token); | ||||
| 	     | ||||
| 	    /* FIXME: this is wrong -- it should really be 0700 owned by
 | ||||
| 	     * the runas user.  That would require some os_ indirection | ||||
| 	     */ | ||||
|             DPRINTF(E_DBG,L_CONF,"Making %s\n",path_buffer); | ||||
|             if((mkdir(path_buffer,0777)) && (errno != EEXIST)) { | ||||
|                 err=errno; | ||||
|                 free(pathdup); | ||||
|                 errno=err; | ||||
| 		DPRINTF(E_LOG,L_CONF,"Could not make dirctory %s: %s\n", | ||||
| 			path_buffer,strerror(errno)); | ||||
| 		return FALSE; | ||||
|             } | ||||
| 
 | ||||
| 	    if(!_conf_existdir(path_buffer)) { | ||||
| 		/* FIXME: this is wrong -- it should really be 0700 owned by
 | ||||
| 		 * the runas user.  That would require some os_ indirection | ||||
| 		 */ | ||||
| 		DPRINTF(E_DBG,L_CONF,"Making %s\n",path_buffer); | ||||
| 		if((mkdir(path_buffer,0700)) && (errno != EEXIST)) { | ||||
| 		    free(pathdup); | ||||
| 		    DPRINTF(E_LOG,L_CONF,"Could not make dirctory %s: %s\n", | ||||
| 			    path_buffer,strerror(errno)); | ||||
| 		    return FALSE; | ||||
| 		} | ||||
| 		os_chown(path_buffer,user); | ||||
| 	    } | ||||
| 	    retval = TRUE; | ||||
| 	} | ||||
|     } | ||||
| @ -285,9 +286,11 @@ int _conf_exists(LL_HANDLE pll, char *section, char *term) { | ||||
|  */ | ||||
| int _conf_verify(LL_HANDLE pll) { | ||||
|     LL_ITEM *pi = NULL; | ||||
|     LL_ITEM *ptemp = NULL; | ||||
|     CONF_ELEMENTS *pce; | ||||
|     int is_valid=TRUE; | ||||
|     char resolved_path[PATH_MAX]; | ||||
|     char *user; | ||||
| 
 | ||||
|     /* first, walk through the elements and make sure
 | ||||
|      * all required elements are there */ | ||||
| @ -309,21 +312,35 @@ int _conf_verify(LL_HANDLE pll) { | ||||
|             } | ||||
|         } | ||||
|         if(pce->type == CONF_T_EXISTPATH) { | ||||
|             /* first, need to resolve */ | ||||
| 	    /* first, need to resolve */ | ||||
|             pi = _conf_fetch_item(pll,pce->section, pce->term); | ||||
|             if(pi) { | ||||
|                 memset(resolved_path,0,sizeof(resolved_path)); | ||||
|                 if(pi->value.as_string) { | ||||
|                     realpath(pi->value.as_string,resolved_path); | ||||
|                     free(pi->value.as_string); | ||||
|                     pi->value.as_string = strdup(resolved_path); | ||||
|                 } | ||||
|                 /* now, should verify it exists */ | ||||
| 		if(!_conf_existdir(resolved_path)) { | ||||
| 		    if(!_conf_makedir(resolved_path)) { | ||||
| 			is_valid=0; | ||||
| 			DPRINTF(E_LOG,L_CONF,"Can't make path %s, invalid config.\n", | ||||
| 				resolved_path); | ||||
| 		    DPRINTF(E_SPAM,L_CONF,"Found %s/%s as %s... checking\n", | ||||
| 			    pce->section, pce->term, pi->value.as_string); | ||||
| 
 | ||||
| 		    /* verify it exists, creating it if necessary */ | ||||
| 		    if(!_conf_existdir(pi->value.as_string)) { | ||||
| 			user = "nobody"; | ||||
| 			ptemp = _conf_fetch_item(pll, "general", "runas"); | ||||
| 			if(ptemp) { | ||||
| 			    user = ptemp->value.as_string; | ||||
| 			} | ||||
| 			 | ||||
| 			if(!_conf_makedir(pi->value.as_string,user)) { | ||||
| 			    is_valid=0; | ||||
| 			    DPRINTF(E_LOG,L_CONF,"Can't make path %s, invalid config.\n", | ||||
| 				    resolved_path); | ||||
| 			} | ||||
| 		    } | ||||
| 
 | ||||
| 		    if(_conf_existdir(pi->value.as_string)) { | ||||
| 			realpath(pi->value.as_string,resolved_path); | ||||
| 			free(pi->value.as_string); | ||||
| 			pi->value.as_string = strdup(resolved_path); | ||||
| 
 | ||||
| 			DPRINTF(E_SPAM,L_CONF,"Resolved to %s\n",resolved_path); | ||||
| 		    } | ||||
| 		} | ||||
|             } | ||||
| @ -520,7 +537,6 @@ int conf_read(char *file) { | ||||
|                     /* this is an inline comment */ | ||||
|                     snprintf(keybuffer,sizeof(keybuffer),"in_%s_%s", | ||||
|                              section_name,term); | ||||
|                     DPRINTF(E_SPAM,L_CONF,"Adding %s: %s\n",keybuffer,comment); | ||||
|                     ll_add_string(pllcomment,keybuffer,comment); | ||||
|                     comment = NULL; | ||||
|                 } | ||||
| @ -529,8 +545,6 @@ int conf_read(char *file) { | ||||
|                     /* we had some preceding comments */ | ||||
|                     snprintf(keybuffer,sizeof(keybuffer),"pre_%s_%s", | ||||
|                              section_name, term); | ||||
|                     DPRINTF(E_SPAM,L_CONF,"Adding %s: %s\n",keybuffer, | ||||
|                                 prev_comments); | ||||
|                     ll_add_string(pllcomment,keybuffer,prev_comments); | ||||
|                     prev_comments[0] = '\0'; | ||||
|                     current_comment_length=0; | ||||
| @ -550,7 +564,6 @@ int conf_read(char *file) { | ||||
|             if(!comment) | ||||
|                 comment = ""; | ||||
| 
 | ||||
|             DPRINTF(E_SPAM,L_CONF,"found comment: %s\n",comment); | ||||
| 
 | ||||
|             /* add to prev comments */ | ||||
|             while((current_comment_length + (int)strlen(comment) + 2 >= | ||||
| @ -578,8 +591,6 @@ int conf_read(char *file) { | ||||
|                     current_comment_length += 2; /* windows, worst case */ | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             DPRINTF(E_SPAM,L_CONF,"Current comment block: \n%s\n",prev_comments); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -727,9 +738,6 @@ char *conf_alloc_string (char *section, char *key, char *dflt) { | ||||
|   conf_get_string(section, key, dflt, NULL, &size); | ||||
|   out = (char *)malloc(size * sizeof(char)); | ||||
| 
 | ||||
|   if(!out) | ||||
|       DPRINTF(E_FATAL,L_CONF,"Malloc failure\n"); | ||||
| 
 | ||||
|   if(conf_get_string (section, key, dflt, out, &size) != CONF_E_SUCCESS) | ||||
|       return NULL; | ||||
| 
 | ||||
|  | ||||
| @ -190,6 +190,40 @@ int os_syslog(int level, char *msg) { | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * os-specific chown | ||||
|  * | ||||
|  *  | ||||
|  */ | ||||
| extern int os_chown(char *path, char *user) { | ||||
|     struct passwd *pw=NULL; | ||||
| 
 | ||||
|     DPRINTF(E_DBG,L_MISC,"Chowning %s to %s\n",path,user); | ||||
| 
 | ||||
|     /* drop privs */ | ||||
|     if(getuid() == (uid_t)0) { | ||||
|         if(atoi(user)) { | ||||
|             pw=getpwuid((uid_t)atoi(user)); /* doh! */ | ||||
|         } else { | ||||
|             pw=getpwnam(user); | ||||
|         } | ||||
| 
 | ||||
|         if(pw) { | ||||
|             if(initgroups(user,pw->pw_gid) != 0 || | ||||
| 	       chown(path, pw->pw_uid, pw->pw_gid) != 0) { | ||||
|                 DPRINTF(E_LOG,L_MISC,"Couldn't chown %s, gid=%d, uid=%d\n", | ||||
|                         user,pw->pw_gid, pw->pw_uid); | ||||
|                 return FALSE; | ||||
|             } | ||||
|         } else { | ||||
|             DPRINTF(E_LOG,L_MISC,"Couldn't lookup user %s for chown\n",user); | ||||
|             return FALSE; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     DPRINTF(E_DBG,L_MISC,"Success!\n"); | ||||
|     return TRUE; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Fork and exit.  Stolen pretty much straight from Stevens. | ||||
|  | ||||
| @ -126,6 +126,14 @@ int os_syslog(int level, char *msg) { | ||||
|     return elog_message(level, msg); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * change the owner of a file to a specific user.  This is | ||||
|  * ignored on windows | ||||
|  */ | ||||
| extern int os_chown(char *path, char *user) { | ||||
|     return TRUE; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int os_register(void) { | ||||
|     service_register(); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user