Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
M
Magisk
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
Magisk
Commits
7955ddce
Commit
7955ddce
authored
Dec 29, 2016
by
topjohnwu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Remove bind, init, and GNU compiler dependancy
parent
42a66ad4
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
93 additions
and
473 deletions
+93
-473
binds.c
binds.c
+0
-246
binds.h
binds.h
+0
-33
daemon.c
daemon.c
+46
-58
su.c
su.c
+46
-135
su.h
su.h
+1
-1
No files found.
binds.c
deleted
100644 → 0
View file @
42a66ad4
/*
Copyright 2016, Pierre-Hugues Husson <phh@phh.me>
This program 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 3 of the License, or
(at your option) any later version.
This program 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 this program; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <sys/select.h>
#include <sys/time.h>
#include <unistd.h>
#include <limits.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <strings.h>
#include <stdint.h>
#include <pwd.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <sys/types.h>
#include <selinux/selinux.h>
#include <arpa/inet.h>
#include "binds.h"
int
bind_foreach
(
bind_cb
cb
,
void
*
arg
)
{
int
res
=
0
;
char
*
str
=
NULL
;
int
fd
=
open
(
"/data/su/binds"
,
O_RDONLY
);
if
(
fd
<
0
)
return
1
;
off_t
size
=
lseek
(
fd
,
0
,
SEEK_END
);
lseek
(
fd
,
0
,
SEEK_SET
);
str
=
malloc
(
size
);
if
(
read
(
fd
,
str
,
size
)
!=
size
)
goto
error
;
char
*
base
=
str
;
while
(
base
<
str
+
size
)
{
char
*
parse_src
,
*
parse_dst
;
int
uid
;
char
*
ptr
=
memchr
(
base
,
0
,
size
-
(
base
-
str
));
if
(
ptr
==
NULL
)
goto
error
;
sscanf
(
base
,
"%d"
,
&
uid
);
parse_src
=
strchr
(
base
,
':'
);
if
(
!
parse_src
)
goto
error
;
parse_src
++
;
parse_dst
=
strchr
(
parse_src
,
':'
);
if
(
!
parse_dst
)
goto
error
;
*
parse_dst
=
0
;
// Split parse_src string
parse_dst
++
;
cb
(
arg
,
uid
,
parse_src
,
parse_dst
);
base
=
ptr
+
1
;
}
res
=
1
;
error:
if
(
str
)
free
(
str
);
close
(
fd
);
return
res
;
}
int
bind_uniq_dst
(
const
char
*
dst
)
{
static
int
_res
;
static
const
char
*
_dst
;
_res
=
1
;
_dst
=
dst
;
auto
void
cb
(
void
*
arg
,
int
uid
,
const
char
*
src
,
const
char
*
dst
)
{
if
(
strcmp
(
dst
,
_dst
)
==
0
)
_res
=
0
;
}
if
(
!
bind_foreach
(
cb
,
NULL
))
return
0
;
return
_res
;
}
void
bind_ls
(
int
uid
)
{
static
int
_uid
;
_uid
=
uid
;
auto
void
cb
(
void
*
arg
,
int
uid
,
const
char
*
src
,
const
char
*
dst
)
{
if
(
_uid
==
0
||
_uid
==
2000
||
_uid
==
uid
)
{
fprintf
(
stderr
,
"%d %s => %s
\n
"
,
uid
,
src
,
dst
);
}
}
bind_foreach
(
cb
,
NULL
);
}
int
bind_remove
(
const
char
*
path
,
int
uid
)
{
static
int
_found
=
0
;
static
const
char
*
_path
;
static
int
_fd
;
static
int
_uid
;
_path
=
path
;
_found
=
0
;
_uid
=
uid
;
unlink
(
"/data/su/bind.new"
);
_fd
=
open
(
"/data/su/bind.new"
,
O_WRONLY
|
O_CREAT
,
0600
);
if
(
_fd
<
0
)
return
0
;
auto
void
cb
(
void
*
arg
,
int
uid
,
const
char
*
src
,
const
char
*
dst
)
{
//The one we want to drop
if
(
strcmp
(
dst
,
_path
)
==
0
&&
(
_uid
==
0
||
_uid
==
2000
||
_uid
==
uid
))
{
_found
=
1
;
return
;
}
char
*
str
=
NULL
;
int
len
=
asprintf
(
&
str
,
"%d:%s:%s"
,
uid
,
src
,
dst
);
write
(
_fd
,
str
,
len
+
1
);
//len doesn't include final \0 and we want to write it
free
(
str
);
}
bind_foreach
(
cb
,
NULL
);
close
(
_fd
);
unlink
(
"/data/su/bind"
);
rename
(
"/data/su/bind.new"
,
"/data/su/bind"
);
return
_found
;
}
int
init_foreach
(
init_cb
icb
,
void
*
arg
)
{
int
res
=
0
;
char
*
str
=
NULL
;
int
fd
=
open
(
"/data/su/init"
,
O_RDONLY
);
if
(
fd
<
0
)
return
1
;
off_t
size
=
lseek
(
fd
,
0
,
SEEK_END
);
lseek
(
fd
,
0
,
SEEK_SET
);
str
=
malloc
(
size
);
if
(
read
(
fd
,
str
,
size
)
!=
size
)
goto
error
;
char
*
base
=
str
;
while
(
base
<
str
+
size
)
{
char
*
parsed
;
int
uid
;
char
*
ptr
=
memchr
(
base
,
0
,
size
-
(
base
-
str
));
if
(
ptr
==
NULL
)
goto
error
;
sscanf
(
base
,
"%d"
,
&
uid
);
parsed
=
strchr
(
base
,
':'
);
if
(
!
parsed
)
goto
error
;
parsed
++
;
icb
(
arg
,
uid
,
parsed
);
base
=
ptr
+
1
;
}
res
=
1
;
error:
if
(
str
)
free
(
str
);
close
(
fd
);
return
res
;
}
int
init_uniq
(
const
char
*
path
)
{
static
int
_res
;
static
const
char
*
_path
;
_res
=
1
;
_path
=
path
;
auto
void
cb
(
void
*
arg
,
int
uid
,
const
char
*
path
)
{
if
(
strcmp
(
path
,
_path
)
==
0
)
_res
=
0
;
}
if
(
!
init_foreach
(
cb
,
NULL
))
return
0
;
return
_res
;
}
int
init_remove
(
const
char
*
path
,
int
uid
)
{
static
int
_found
=
0
;
static
const
char
*
_path
;
static
int
fd
;
static
int
_uid
;
_path
=
path
;
_found
=
0
;
_uid
=
uid
;
unlink
(
"/data/su/init.new"
);
fd
=
open
(
"/data/su/init.new"
,
O_WRONLY
|
O_CREAT
,
0600
);
if
(
fd
<
0
)
return
0
;
auto
void
cb
(
void
*
arg
,
int
uid
,
const
char
*
path
)
{
//The one we want to drop
if
(
strcmp
(
path
,
_path
)
==
0
&&
(
_uid
==
0
||
_uid
==
2000
||
uid
==
_uid
))
{
_found
=
1
;
return
;
}
char
*
str
=
NULL
;
int
len
=
asprintf
(
&
str
,
"%d:%s"
,
uid
,
path
);
write
(
fd
,
str
,
len
+
1
);
//len doesn't include final \0 and we want to write it
free
(
str
);
}
init_foreach
(
cb
,
NULL
);
close
(
fd
);
unlink
(
"/data/su/init"
);
rename
(
"/data/su/init.new"
,
"/data/su/init"
);
return
_found
;
}
void
init_ls
(
int
uid
)
{
static
int
_uid
;
_uid
=
uid
;
auto
void
cb
(
void
*
arg
,
int
uid
,
const
char
*
path
)
{
if
(
_uid
==
2000
||
_uid
==
0
||
_uid
==
uid
)
fprintf
(
stderr
,
"%d %s
\n
"
,
uid
,
path
);
}
init_foreach
(
cb
,
NULL
);
}
binds.h
deleted
100644 → 0
View file @
42a66ad4
/*
Copyright 2016, Pierre-Hugues Husson <phh@phh.me>
This program 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 3 of the License, or
(at your option) any later version.
This program 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 this program; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef BINDSH
#define BINDS_H
typedef
void
(
*
bind_cb
)(
void
*
arg
,
int
uid
,
const
char
*
src
,
const
char
*
dst
);
extern
int
bind_foreach
(
bind_cb
cb
,
void
*
arg
);
extern
int
bind_uniq_dst
(
const
char
*
dst
);
extern
int
bind_remove
(
const
char
*
path
,
int
uid
);
extern
void
bind_ls
(
int
uid
);
typedef
void
(
*
init_cb
)(
void
*
arg
,
int
uid
,
const
char
*
path
);
extern
int
init_foreach
(
init_cb
cb
,
void
*
arg
);
extern
int
init_uniq
(
const
char
*
dst
);
extern
int
init_remove
(
const
char
*
path
,
int
uid
);
extern
void
init_ls
(
int
uid
);
#endif
/* BINDS_H */
daemon.c
View file @
7955ddce
...
...
@@ -47,7 +47,6 @@
#include <cutils/multiuser.h>
#endif
#include "binds.h"
#include "su.h"
#include "utils.h"
#include "pts.h"
...
...
@@ -499,79 +498,68 @@ static void prepare_su_bind() {
}
}
static
void
prepare_binds
()
{
mkdir
(
"/data/su"
,
0700
);
static
int
i
=
0
;
static
void
bind_cb_func
(
void
*
arg
,
int
uid
,
const
char
*
src
,
const
char
*
dst
)
{
int
ret
=
0
,
i
=
0
;
auto
void
cb
(
void
*
arg
,
int
uid
,
const
char
*
src
,
const
char
*
dst
)
{
int
ret
=
0
;
char
*
tmpfile
=
NULL
;
asprintf
(
&
tmpfile
,
"/dev/su/bind%d"
,
i
++
);
struct
stat
stbuf
;
ret
=
stat
(
src
,
&
stbuf
);
if
(
ret
)
{
free
(
tmpfile
);
LOGE
(
"Failed to stat src %s file"
,
src
);
return
;
}
char
*
tmpfile
=
NULL
;
asprintf
(
&
tmpfile
,
"/dev/su/bind%d"
,
i
++
);
struct
stat
stbuf
;
ret
=
stat
(
src
,
&
stbuf
);
if
(
ret
)
{
free
(
tmpfile
);
LOGE
(
"Failed to stat src %s file"
,
src
);
return
;
}
//Only shell uid is allowed to bind files not his own
if
(
uid
!=
2000
&&
uid
!=
stbuf
.
st_uid
)
{
LOGE
(
"File %s has wrong owner: %d vs %d"
,
src
,
uid
,
stbuf
.
st_uid
);
return
;
}
//Only shell uid is allowed to bind files not his own
if
(
uid
!=
2000
&&
uid
!=
stbuf
.
st_uid
)
{
LOGE
(
"File %s has wrong owner: %d vs %d"
,
src
,
uid
,
stbuf
.
st_uid
);
return
;
}
ret
=
copy_file
(
src
,
tmpfile
,
0
);
if
(
ret
)
{
free
(
tmpfile
);
PLOGE
(
"Failed to copy su"
);
return
;
}
chmod
(
tmpfile
,
stbuf
.
st_mode
);
ret
=
copy_file
(
src
,
tmpfile
,
0
);
if
(
ret
)
{
free
(
tmpfile
);
PLOGE
(
"Failed to copy su"
);
return
;
}
chmod
(
tmpfile
,
stbuf
.
st_mode
);
ret
=
setfilecon
(
tmpfile
,
"u:object_r:system_file:s0"
);
if
(
ret
)
{
LOGE
(
"Failed to set file context"
);
return
;
}
ret
=
setfilecon
(
tmpfile
,
"u:object_r:system_file:s0"
);
if
(
ret
)
{
LOGE
(
"Failed to set file context"
);
return
;
}
ret
=
mount
(
tmpfile
,
dst
,
""
,
MS_BIND
,
NULL
);
if
(
ret
)
{
LOGE
(
"Failed to mount bind"
);
return
;
}
}
bind_foreach
(
cb
,
NULL
);
ret
=
mount
(
tmpfile
,
dst
,
""
,
MS_BIND
,
NULL
);
if
(
ret
)
{
LOGE
(
"Failed to mount bind"
);
return
;
}
}
static
void
do_init
()
{
auto
void
cb
(
void
*
arg
,
int
uid
,
const
char
*
path
)
{
int
ret
=
0
;
static
void
init_cb_func
(
void
*
arg
,
int
uid
,
const
char
*
path
)
{
int
ret
=
0
;
int
p
=
fork
();
if
(
p
)
return
;
int
p
=
fork
();
if
(
p
)
return
;
while
(
access
(
"/system/bin/sh"
,
R_OK
))
sleep
(
1
);
ret
=
setexeccon
(
"u:r:su:s0"
);
execl
(
path
,
path
,
NULL
);
LOGE
(
"Failed to execute %s. Trying as shell script, ret = %d"
,
path
,
ret
);
while
(
access
(
"/system/bin/sh"
,
R_OK
))
sleep
(
1
);
ret
=
setexeccon
(
"u:r:su:s0"
);
execl
(
path
,
path
,
NULL
);
LOGE
(
"Failed to execute %s. Trying as shell script, ret = %d"
,
path
,
ret
);
ret
=
setexeccon
(
"u:r:su:s0"
);
execl
(
"/system/bin/sh"
,
"/system/bin/sh"
,
path
,
NULL
);
LOGE
(
"Failed to execute %s as shell script"
,
path
);
_exit
(
1
);
}
init_foreach
(
cb
,
NULL
);
ret
=
setexeccon
(
"u:r:su:s0"
);
execl
(
"/system/bin/sh"
,
"/system/bin/sh"
,
path
,
NULL
);
LOGE
(
"Failed to execute %s as shell script"
,
path
);
_exit
(
1
);
}
static
void
prepare
()
{
setfscreatecon
(
"u:object_r:su_daemon:s0"
);
mkdir
(
"/dev/su"
,
0700
);
prepare_su_bind
();
prepare_binds
();
do_init
();
setfscreatecon
(
NULL
);
}
...
...
su.c
View file @
7955ddce
...
...
@@ -42,7 +42,6 @@
#include "su.h"
#include "utils.h"
#include "binds.h"
extern
int
is_daemon
;
extern
int
daemon_from_uid
;
...
...
@@ -470,70 +469,6 @@ static void usage(int status) {
exit
(
status
);
}
static
__attribute__
((
noreturn
))
void
allow_bind
(
struct
su_context
*
ctx
)
{
if
(
ctx
->
from
.
uid
==
0
)
exit
(
1
);
if
(
ctx
->
bind
.
from
[
0
]
==
'!'
)
{
int
ret
=
bind_remove
(
ctx
->
bind
.
to
,
ctx
->
from
.
uid
);
if
(
!
ret
)
{
fprintf
(
stderr
,
"The mentioned bind destination path didn't exist
\n
"
);
exit
(
1
);
}
exit
(
0
);
}
if
(
strcmp
(
"--ls"
,
ctx
->
bind
.
from
)
==
0
)
{
bind_ls
(
ctx
->
from
.
uid
);
exit
(
0
);
}
if
(
!
bind_uniq_dst
(
ctx
->
bind
.
to
))
{
fprintf
(
stderr
,
"BIND: Distant file NOT unique. I refuse.
\n
"
);
exit
(
1
);
}
int
fd
=
open
(
"/data/su/binds"
,
O_WRONLY
|
O_APPEND
|
O_CREAT
,
0600
);
if
(
fd
<
0
)
{
fprintf
(
stderr
,
"Failed to open binds file
\n
"
);
exit
(
1
);
}
char
*
str
=
NULL
;
int
len
=
asprintf
(
&
str
,
"%d:%s:%s"
,
ctx
->
from
.
uid
,
ctx
->
bind
.
from
,
ctx
->
bind
.
to
);
write
(
fd
,
str
,
len
+
1
);
//len doesn't include final \0 and we want to write it
free
(
str
);
close
(
fd
);
exit
(
0
);
}
static
__attribute__
((
noreturn
))
void
allow_init
(
struct
su_context
*
ctx
)
{
if
(
ctx
->
init
[
0
]
==
'!'
)
{
int
ret
=
init_remove
(
ctx
->
init
+
1
,
ctx
->
from
.
uid
);
if
(
!
ret
)
{
fprintf
(
stderr
,
"The mentioned init path didn't exist
\n
"
);
exit
(
1
);
}
exit
(
0
);
}
if
(
strcmp
(
"--ls"
,
ctx
->
init
)
==
0
)
{
init_ls
(
ctx
->
from
.
uid
);
exit
(
0
);
}
if
(
!
init_uniq
(
ctx
->
init
))
//This script is already in init list
exit
(
1
);
int
fd
=
open
(
"/data/su/init"
,
O_WRONLY
|
O_APPEND
|
O_CREAT
,
0600
);
if
(
fd
<
0
)
{
fprintf
(
stderr
,
"Failed to open init file
\n
"
);
exit
(
1
);
}
char
*
str
=
NULL
;
int
len
=
asprintf
(
&
str
,
"%d:%s"
,
ctx
->
from
.
uid
,
ctx
->
init
);
write
(
fd
,
str
,
len
+
1
);
//len doesn't include final \0 and we want to write it
free
(
str
);
close
(
fd
);
exit
(
0
);
}
static
__attribute__
((
noreturn
))
void
deny
(
struct
su_context
*
ctx
)
{
char
*
cmd
=
get_command
(
&
ctx
->
to
);
...
...
@@ -575,11 +510,11 @@ static __attribute__ ((noreturn)) void allow(struct su_context *ctx) {
if
(
send_to_app
)
send_result
(
ctx
,
ALLOW
);
if
(
ctx
->
bind
.
from
[
0
]
&&
ctx
->
bind
.
to
[
0
])
allow_bind
(
ctx
);
//
if(ctx->bind.from[0] && ctx->bind.to[0])
//
allow_bind(ctx);
if
(
ctx
->
init
[
0
])
allow_init
(
ctx
);
//
if(ctx->init[0])
//
allow_init(ctx);
char
*
binary
;
argc
=
ctx
->
to
.
optind
;
...
...
@@ -777,21 +712,14 @@ int su_main_nodaemon(int argc, char **argv) {
.
database_path
=
REQUESTOR_DATA_PATH
REQUESTOR_DATABASE_PATH
,
.
base_path
=
REQUESTOR_DATA_PATH
REQUESTOR
},
.
bind
=
{
.
from
=
""
,
.
to
=
""
,
},
.
init
=
""
,
};
struct
stat
st
;
int
c
,
socket_serv_fd
,
fd
;
char
buf
[
64
],
*
result
;
policy_t
dballow
;
struct
option
long_opts
[]
=
{
{
"bind"
,
required_argument
,
NULL
,
'b'
},
{
"command"
,
required_argument
,
NULL
,
'c'
},
{
"help"
,
no_argument
,
NULL
,
'h'
},
{
"init"
,
required_argument
,
NULL
,
'i'
},
{
"login"
,
no_argument
,
NULL
,
'l'
},
{
"preserve-environment"
,
no_argument
,
NULL
,
'p'
},
{
"shell"
,
required_argument
,
NULL
,
's'
},
...
...
@@ -800,71 +728,54 @@ int su_main_nodaemon(int argc, char **argv) {
{
NULL
,
0
,
NULL
,
0
},
};
while
((
c
=
getopt_long
(
argc
,
argv
,
"
+b:
c:hlmps:Vvuz:"
,
long_opts
,
NULL
))
!=
-
1
)
{
while
((
c
=
getopt_long
(
argc
,
argv
,
"c:hlmps:Vvuz:"
,
long_opts
,
NULL
))
!=
-
1
)
{
switch
(
c
)
{
case
'b'
:
{
char
*
s
=
strdup
(
optarg
);
char
*
pos
=
strchr
(
s
,
':'
);
if
(
pos
)
{
pos
[
0
]
=
0
;
ctx
.
bind
.
to
=
pos
+
1
;
ctx
.
bind
.
from
=
s
;
}
else
{
ctx
.
bind
.
from
=
"--ls"
;
ctx
.
bind
.
to
=
"--ls"
;
}
}
case
'c'
:
ctx
.
to
.
shell
=
DEFAULT_SHELL
;
ctx
.
to
.
command
=
optarg
;
break
;
case
'c'
:
ctx
.
to
.
shell
=
DEFAULT_SHELL
;
ctx
.
to
.
command
=
optarg
;
break
;
case
'h'
:
usage
(
EXIT_SUCCESS
);
break
;
case
'i'
:
ctx
.
init
=
optarg
;
break
;
case
'l'
:
ctx
.
to
.
login
=
1
;
break
;
case
'm'
:
case
'p'
:
ctx
.
to
.
keepenv
=
1
;
break
;
case
's'
:
ctx
.
to
.
shell
=
optarg
;
break
;
case
'V'
:
printf
(
"%d
\n
"
,
VERSION_CODE
);
exit
(
EXIT_SUCCESS
);
case
'v'
:
printf
(
"%s cm-su subind suinit
\n
"
,
VERSION
);
exit
(
EXIT_SUCCESS
);
case
'u'
:
switch
(
get_multiuser_mode
())
{
case
MULTIUSER_MODE_USER
:
printf
(
"%s
\n
"
,
MULTIUSER_VALUE_USER
);
case
'h'
:
usage
(
EXIT_SUCCESS
);
break
;
case
MULTIUSER_MODE_OWNER_MANAGED
:
printf
(
"%s
\n
"
,
MULTIUSER_VALUE_OWNER_MANAGED
)
;
case
'l'
:
ctx
.
to
.
login
=
1
;
break
;
case
MULTIUSER_MODE_OWNER_ONLY
:
printf
(
"%s
\n
"
,
MULTIUSER_VALUE_OWNER_ONLY
);
case
'm'
:
case
'p'
:
ctx
.
to
.
keepenv
=
1
;
break
;
case
MULTIUSER_MODE_NONE
:
printf
(
"%s
\n
"
,
MULTIUSER_VALUE_NONE
)
;
case
's'
:
ctx
.
to
.
shell
=
optarg
;
break
;
}
exit
(
EXIT_SUCCESS
);
case
'z'
:
ctx
.
to
.
context
=
optarg
;
break
;
default:
/* Bionic getopt_long doesn't terminate its error output by newline */
fprintf
(
stderr
,
"
\n
"
);
usage
(
2
);
case
'V'
:
printf
(
"%d
\n
"
,
VERSION_CODE
);
exit
(
EXIT_SUCCESS
);
case
'v'
:
printf
(
"%s (topjohnwu v1)
\n
"
,
VERSION
);
exit
(
EXIT_SUCCESS
);
case
'u'
:
switch
(
get_multiuser_mode
())
{
case
MULTIUSER_MODE_USER
:
printf
(
"%s
\n
"
,
MULTIUSER_VALUE_USER
);
break
;
case
MULTIUSER_MODE_OWNER_MANAGED
:
printf
(
"%s
\n
"
,
MULTIUSER_VALUE_OWNER_MANAGED
);
break
;
case
MULTIUSER_MODE_OWNER_ONLY
:
printf
(
"%s
\n
"
,
MULTIUSER_VALUE_OWNER_ONLY
);
break
;
case
MULTIUSER_MODE_NONE
:
printf
(
"%s
\n
"
,
MULTIUSER_VALUE_NONE
);
break
;
}
exit
(
EXIT_SUCCESS
);
case
'z'
:
ctx
.
to
.
context
=
optarg
;
break
;
default:
/* Bionic getopt_long doesn't terminate its error output by newline */
fprintf
(
stderr
,
"
\n
"
);
usage
(
2
);
}
}
hacks_init
();
...
...
su.h
View file @
7955ddce
...
...
@@ -77,7 +77,7 @@
#define str(a) #a
#ifndef VERSION_CODE
#define VERSION_CODE 1
7
#define VERSION_CODE 1
#endif
#define VERSION xstr(VERSION_CODE) " " REQUESTOR
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment