2010-01-23 03:52:57 +03:00
/*-*- Mode: C; c-basic-offset: 8 -*-*/
2010-02-03 15:03:47 +03:00
/***
This file is part of systemd .
Copyright 2010 Lennart Poettering
systemd is free software ; you can redistribute it and / or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
systemd is distributed in the hope that it will be useful , but
WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
General Public License for more details .
You should have received a copy of the GNU General Public License
along with systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
2010-01-23 03:52:57 +03:00
# include <errno.h>
2010-01-29 04:07:41 +03:00
# include <stdio.h>
# include <mntent.h>
2010-01-29 08:04:08 +03:00
# include <sys/epoll.h>
# include <sys/poll.h>
2010-01-23 03:52:57 +03:00
2010-01-26 23:39:06 +03:00
# include "unit.h"
2010-01-23 03:52:57 +03:00
# include "mount.h"
# include "load-fragment.h"
# include "load-dropin.h"
2010-01-29 04:07:41 +03:00
# include "log.h"
2010-01-23 03:52:57 +03:00
2010-01-29 05:18:09 +03:00
static const UnitActiveState state_translation_table [ _MOUNT_STATE_MAX ] = {
[ MOUNT_DEAD ] = UNIT_INACTIVE ,
[ MOUNT_MOUNTING ] = UNIT_ACTIVATING ,
[ MOUNT_MOUNTED ] = UNIT_ACTIVE ,
[ MOUNT_UNMOUNTING ] = UNIT_DEACTIVATING ,
[ MOUNT_MAINTAINANCE ] = UNIT_INACTIVE ,
} ;
2010-01-23 03:52:57 +03:00
2010-01-29 05:18:09 +03:00
static const char * const state_string_table [ _MOUNT_STATE_MAX ] = {
[ MOUNT_DEAD ] = " dead " ,
[ MOUNT_MOUNTING ] = " mounting " ,
[ MOUNT_MOUNTED ] = " mounted " ,
[ MOUNT_UNMOUNTING ] = " unmounting " ,
[ MOUNT_MAINTAINANCE ] = " maintainance "
} ;
2010-01-23 03:52:57 +03:00
2010-01-26 23:39:06 +03:00
static void mount_done ( Unit * u ) {
2010-01-29 08:04:08 +03:00
Mount * m = MOUNT ( u ) ;
2010-01-26 06:18:44 +03:00
2010-01-29 08:04:08 +03:00
assert ( m ) ;
free ( m - > what ) ;
free ( m - > where ) ;
2010-01-26 06:18:44 +03:00
}
2010-01-29 05:18:09 +03:00
static void mount_set_state ( Mount * m , MountState state ) {
MountState old_state ;
assert ( m ) ;
2010-01-29 08:04:08 +03:00
if ( state = = m - > state )
return ;
2010-01-29 05:18:09 +03:00
old_state = m - > state ;
m - > state = state ;
log_debug ( " %s changed %s → %s " , unit_id ( UNIT ( m ) ) , state_string_table [ old_state ] , state_string_table [ state ] ) ;
unit_notify ( UNIT ( m ) , state_translation_table [ old_state ] , state_translation_table [ state ] ) ;
}
static int mount_coldplug ( Unit * u ) {
Mount * m = MOUNT ( u ) ;
assert ( m ) ;
assert ( m - > state = = MOUNT_DEAD ) ;
2010-01-23 03:52:57 +03:00
2010-01-29 05:18:09 +03:00
if ( m - > from_proc_self_mountinfo )
mount_set_state ( m , MOUNT_MOUNTED ) ;
2010-01-23 03:52:57 +03:00
2010-01-29 05:18:09 +03:00
return 0 ;
}
static void mount_dump ( Unit * u , FILE * f , const char * prefix ) {
2010-01-26 23:39:06 +03:00
Mount * s = MOUNT ( u ) ;
2010-01-23 03:52:57 +03:00
assert ( s ) ;
fprintf ( f ,
" %sMount State: %s \n "
2010-01-29 04:07:41 +03:00
" %sWhere: %s \n "
2010-01-29 05:18:09 +03:00
" %sWhat: %s \n "
" %sFrom /etc/fstab: %s \n "
" %sFrom /proc/self/mountinfo: %s \n " ,
prefix , state_string_table [ s - > state ] ,
2010-01-29 04:07:41 +03:00
prefix , s - > where ,
2010-01-29 05:18:09 +03:00
prefix , s - > what ,
prefix , yes_no ( s - > from_etc_fstab ) ,
prefix , yes_no ( s - > from_proc_self_mountinfo ) ) ;
}
static UnitActiveState mount_active_state ( Unit * u ) {
assert ( u ) ;
return state_translation_table [ MOUNT ( u ) - > state ] ;
2010-01-29 04:07:41 +03:00
}
static void mount_shutdown ( Manager * m ) {
2010-01-29 08:04:08 +03:00
assert ( m ) ;
if ( m - > proc_self_mountinfo )
fclose ( m - > proc_self_mountinfo ) ;
2010-01-29 04:07:41 +03:00
}
static int mount_add_node_links ( Mount * m ) {
Unit * device ;
char * e ;
int r ;
assert ( m ) ;
/* Adds in links to the device that this node is based on */
if ( ! path_startswith ( m - > what , " /dev/ " ) )
return 0 ;
2010-02-14 03:02:46 +03:00
if ( ! ( e = unit_name_escape_path ( m - > what + 1 , " .device " ) ) )
2010-01-29 04:07:41 +03:00
return - ENOMEM ;
r = manager_load_unit ( UNIT ( m ) - > meta . manager , e , & device ) ;
free ( e ) ;
if ( r < 0 )
return r ;
if ( ( r = unit_add_dependency ( UNIT ( m ) , UNIT_AFTER , device ) ) < 0 )
return r ;
if ( ( r = unit_add_dependency ( UNIT ( m ) , UNIT_REQUIRES , device ) ) < 0 )
return r ;
if ( ( r = unit_add_dependency ( device , UNIT_WANTS , UNIT ( m ) ) ) < 0 )
return r ;
return 0 ;
}
static int mount_add_path_links ( Mount * m ) {
2010-01-29 08:04:08 +03:00
Meta * other ;
2010-01-29 04:07:41 +03:00
int r ;
/* Adds in link to other mount points, that might lie below or
* above us in the hierarchy */
2010-01-29 08:04:08 +03:00
LIST_FOREACH ( units_per_type , other , UNIT ( m ) - > meta . manager - > units_per_type [ UNIT_MOUNT ] ) {
2010-01-29 04:07:41 +03:00
Mount * n ;
2010-01-29 08:04:08 +03:00
n = ( Mount * ) other ;
2010-01-29 04:07:41 +03:00
2010-01-29 08:04:08 +03:00
if ( n = = m )
2010-02-14 03:09:22 +03:00
continue ;
2010-01-29 04:07:41 +03:00
if ( path_startswith ( m - > where , n - > where ) ) {
2010-01-29 08:04:08 +03:00
if ( ( r = unit_add_dependency ( UNIT ( m ) , UNIT_AFTER , ( Unit * ) other ) ) < 0 )
2010-01-29 04:07:41 +03:00
return r ;
2010-01-29 08:04:08 +03:00
if ( ( r = unit_add_dependency ( UNIT ( m ) , UNIT_REQUIRES , ( Unit * ) other ) ) < 0 )
2010-01-29 04:07:41 +03:00
return r ;
} else if ( startswith ( n - > where , m - > where ) ) {
2010-01-29 08:04:08 +03:00
if ( ( r = unit_add_dependency ( UNIT ( m ) , UNIT_BEFORE , ( Unit * ) other ) ) < 0 )
2010-01-29 04:07:41 +03:00
return r ;
2010-01-29 08:04:08 +03:00
if ( ( r = unit_add_dependency ( ( Unit * ) other , UNIT_REQUIRES , UNIT ( m ) ) ) < 0 )
2010-01-29 04:07:41 +03:00
return r ;
}
}
return 0 ;
}
2010-01-29 08:04:08 +03:00
static int mount_add_one ( Manager * m , const char * what , const char * where , bool live , bool set_flags ) {
2010-01-29 04:07:41 +03:00
char * e ;
int r ;
Unit * u ;
bool delete ;
2010-01-29 05:18:09 +03:00
assert ( m ) ;
2010-01-29 04:07:41 +03:00
assert ( what ) ;
assert ( where ) ;
/* probably some kind of swap, which we don't cover for now */
if ( where [ 0 ] ! = ' / ' )
return 0 ;
if ( streq ( where , " / " ) )
2010-02-14 03:02:46 +03:00
e = strdup ( " -.mount " ) ;
2010-01-29 04:07:41 +03:00
else
2010-02-14 03:02:46 +03:00
e = unit_name_escape_path ( where + 1 , " .mount " ) ;
2010-01-29 04:07:41 +03:00
if ( ! e )
return - ENOMEM ;
if ( ! ( u = manager_get_unit ( m , e ) ) ) {
delete = true ;
if ( ! ( u = unit_new ( m ) ) ) {
free ( e ) ;
return - ENOMEM ;
}
r = unit_add_name ( u , e ) ;
free ( e ) ;
if ( r < 0 )
goto fail ;
if ( ! ( MOUNT ( u ) - > what = strdup ( what ) ) | |
2010-01-29 05:18:09 +03:00
! ( MOUNT ( u ) - > where = strdup ( where ) ) ) {
r = - ENOMEM ;
goto fail ;
}
if ( ( r = unit_set_description ( u , where ) ) < 0 )
2010-01-29 04:07:41 +03:00
goto fail ;
2010-01-29 08:04:08 +03:00
2010-01-29 08:45:59 +03:00
unit_add_to_load_queue ( u ) ;
2010-01-29 04:07:41 +03:00
} else {
delete = false ;
free ( e ) ;
}
2010-01-29 08:04:08 +03:00
if ( set_flags )
MOUNT ( u ) - > still_exists = true ;
if ( live ) {
if ( set_flags )
MOUNT ( u ) - > just_created = ! MOUNT ( u ) - > from_proc_self_mountinfo ;
2010-01-29 05:18:09 +03:00
MOUNT ( u ) - > from_proc_self_mountinfo = true ;
2010-01-29 08:04:08 +03:00
} else {
if ( set_flags )
MOUNT ( u ) - > just_created = ! MOUNT ( u ) - > from_etc_fstab ;
2010-01-29 05:18:09 +03:00
MOUNT ( u ) - > from_etc_fstab = true ;
2010-01-29 08:04:08 +03:00
}
2010-01-29 05:18:09 +03:00
2010-01-29 04:07:41 +03:00
if ( ( r = mount_add_node_links ( MOUNT ( u ) ) ) < 0 )
goto fail ;
if ( ( r = mount_add_path_links ( MOUNT ( u ) ) ) < 0 )
goto fail ;
2010-02-05 02:38:41 +03:00
unit_add_to_dbus_queue ( u ) ;
2010-01-29 04:07:41 +03:00
return 0 ;
fail :
if ( delete & & u )
unit_free ( u ) ;
return 0 ;
}
static char * fstab_node_to_udev_node ( char * p ) {
char * dn , * t ;
int r ;
/* FIXME: to follow udev's logic 100% we need to leave valid
* UTF8 chars unescaped */
if ( startswith ( p , " LABEL= " ) ) {
2010-01-29 05:18:09 +03:00
if ( ! ( t = xescape ( p + 6 , " / " ) ) )
2010-01-29 04:07:41 +03:00
return NULL ;
2010-01-29 05:18:09 +03:00
r = asprintf ( & dn , " /dev/disk/by-label/%s " , t ) ;
2010-01-29 04:07:41 +03:00
free ( t ) ;
if ( r < 0 )
return NULL ;
return dn ;
}
if ( startswith ( p , " UUID= " ) ) {
2010-01-29 05:18:09 +03:00
if ( ! ( t = xescape ( p + 5 , " / " ) ) )
2010-01-29 04:07:41 +03:00
return NULL ;
2010-01-29 05:18:09 +03:00
r = asprintf ( & dn , " /dev/disk/by-uuid/%s " , ascii_strlower ( t ) ) ;
2010-01-29 04:07:41 +03:00
free ( t ) ;
if ( r < 0 )
return NULL ;
return dn ;
}
return strdup ( p ) ;
}
2010-01-29 08:04:08 +03:00
static int mount_load_etc_fstab ( Manager * m , bool set_flags ) {
2010-01-29 04:07:41 +03:00
FILE * f ;
int r ;
struct mntent * me ;
assert ( m ) ;
errno = 0 ;
if ( ! ( f = setmntent ( " /etc/fstab " , " r " ) ) )
return - errno ;
while ( ( me = getmntent ( f ) ) ) {
char * where , * what ;
if ( ! ( what = fstab_node_to_udev_node ( me - > mnt_fsname ) ) ) {
r = - ENOMEM ;
goto finish ;
}
if ( ! ( where = strdup ( me - > mnt_dir ) ) ) {
free ( what ) ;
r = - ENOMEM ;
goto finish ;
}
if ( what [ 0 ] = = ' / ' )
path_kill_slashes ( what ) ;
if ( where [ 0 ] = = ' / ' )
path_kill_slashes ( where ) ;
2010-01-29 08:04:08 +03:00
r = mount_add_one ( m , what , where , false , set_flags ) ;
2010-01-29 04:07:41 +03:00
free ( what ) ;
free ( where ) ;
if ( r < 0 )
goto finish ;
}
r = 0 ;
finish :
endmntent ( f ) ;
return r ;
}
2010-01-29 08:04:08 +03:00
static int mount_load_proc_self_mountinfo ( Manager * m , bool set_flags ) {
2010-01-29 04:07:41 +03:00
int r ;
assert ( m ) ;
2010-01-29 08:04:08 +03:00
rewind ( m - > proc_self_mountinfo ) ;
2010-01-29 04:07:41 +03:00
for ( ; ; ) {
int k ;
char * device , * path , * d , * p ;
2010-01-29 08:04:08 +03:00
if ( ( k = fscanf ( m - > proc_self_mountinfo ,
2010-01-29 04:07:41 +03:00
" %*s " /* (1) mount id */
" %*s " /* (2) parent id */
" %*s " /* (3) major:minor */
" %*s " /* (4) root */
" %ms " /* (5) mount point */
" %*s " /* (6) mount options */
" %*[^-] " /* (7) optional fields */
" - " /* (8) seperator */
" %*s " /* (9) file system type */
2010-01-29 08:04:08 +03:00
" %ms " /* (10) mount source */
" %*[^ \n ] " , /* some rubbish at the end */
2010-01-29 04:07:41 +03:00
& path ,
& device ) ) ! = 2 ) {
2010-01-29 08:04:08 +03:00
if ( k = = EOF )
break ;
2010-01-29 04:07:41 +03:00
2010-01-29 08:04:08 +03:00
return - EBADMSG ;
2010-01-29 04:07:41 +03:00
}
if ( ! ( d = cunescape ( device ) ) ) {
free ( device ) ;
free ( path ) ;
2010-01-29 08:04:08 +03:00
return - ENOMEM ;
2010-01-29 04:07:41 +03:00
}
free ( device ) ;
if ( ! ( p = cunescape ( path ) ) ) {
free ( d ) ;
free ( path ) ;
2010-01-29 08:04:08 +03:00
return - ENOMEM ;
2010-01-29 04:07:41 +03:00
}
free ( path ) ;
2010-01-29 08:04:08 +03:00
r = mount_add_one ( m , d , p , true , set_flags ) ;
2010-01-29 04:07:41 +03:00
free ( d ) ;
free ( p ) ;
if ( r < 0 )
2010-01-29 08:04:08 +03:00
return r ;
2010-01-29 04:07:41 +03:00
}
2010-01-29 08:04:08 +03:00
return 0 ;
2010-01-29 04:07:41 +03:00
}
static int mount_enumerate ( Manager * m ) {
int r ;
2010-01-29 08:04:08 +03:00
struct epoll_event ev ;
2010-01-29 04:07:41 +03:00
assert ( m ) ;
2010-01-29 08:04:08 +03:00
if ( ! ( m - > proc_self_mountinfo = fopen ( " /proc/self/mountinfo " , " r " ) ) )
return - errno ;
m - > mount_watch . type = WATCH_MOUNT ;
m - > mount_watch . fd = fileno ( m - > proc_self_mountinfo ) ;
zero ( ev ) ;
ev . events = EPOLLERR ;
ev . data . ptr = & m - > mount_watch ;
if ( epoll_ctl ( m - > epoll_fd , EPOLL_CTL_ADD , m - > mount_watch . fd , & ev ) < 0 )
return - errno ;
if ( ( r = mount_load_etc_fstab ( m , false ) ) < 0 )
2010-01-29 04:07:41 +03:00
goto fail ;
2010-01-29 08:04:08 +03:00
if ( ( r = mount_load_proc_self_mountinfo ( m , false ) ) < 0 )
2010-01-29 04:07:41 +03:00
goto fail ;
return 0 ;
fail :
mount_shutdown ( m ) ;
return r ;
2010-01-23 03:52:57 +03:00
}
2010-01-29 08:04:08 +03:00
void mount_fd_event ( Manager * m , int events ) {
Meta * meta ;
int r ;
assert ( m ) ;
2010-01-29 08:45:59 +03:00
assert ( events = = EPOLLERR ) ;
2010-01-29 08:04:08 +03:00
/* The manager calls this for every fd event happening on the
* / proc / self / mountinfo file , which informs us about mounting
* table changes */
if ( ( r = mount_load_proc_self_mountinfo ( m , true ) ) < 0 ) {
log_error ( " Failed to reread /proc/self/mountinfo: %s " , strerror ( - errno ) ) ;
return ;
}
manager_dispatch_load_queue ( m ) ;
LIST_FOREACH ( units_per_type , meta , m - > units_per_type [ UNIT_MOUNT ] ) {
Mount * mount = ( Mount * ) meta ;
if ( mount - > just_created & & mount - > state = = MOUNT_DEAD )
mount_set_state ( mount , MOUNT_MOUNTED ) ;
else if ( ! mount - > still_exists & & mount - > state = = MOUNT_MOUNTED ) {
mount_set_state ( mount , MOUNT_DEAD ) ;
mount - > from_proc_self_mountinfo = false ;
}
/* Clear the flags for later calls */
mount - > just_created = false ;
mount - > still_exists = false ;
}
}
2010-01-26 23:39:06 +03:00
const UnitVTable mount_vtable = {
2010-01-23 03:52:57 +03:00
. suffix = " .mount " ,
2010-04-06 04:43:58 +04:00
. init = unit_load_fragment_and_dropin_optional ,
2010-01-26 06:18:44 +03:00
. done = mount_done ,
2010-01-29 05:18:09 +03:00
. coldplug = mount_coldplug ,
2010-01-26 06:18:44 +03:00
. dump = mount_dump ,
2010-01-23 03:52:57 +03:00
2010-01-29 05:18:09 +03:00
. active_state = mount_active_state ,
2010-01-29 04:07:41 +03:00
2010-01-29 05:18:09 +03:00
. enumerate = mount_enumerate ,
. shutdown = mount_shutdown
2010-01-23 03:52:57 +03:00
} ;