Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
H
hsh-maxima
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Deploy
Releases
Package registry
Container registry
Model registry
Operate
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
elc
hsh-maxima
Commits
fafb4ad0
Commit
fafb4ad0
authored
5 years ago
by
lennart
Browse files
Options
Downloads
Patches
Plain Diff
implement faster fork-based way
parent
c39d5b40
No related branches found
No related tags found
No related merge requests found
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
Dockerfile
+19
-14
19 additions, 14 deletions
Dockerfile
assets/maxima-fork.lisp
+29
-0
29 additions, 0 deletions
assets/maxima-fork.lisp
assets/optimize.mac.template
+1
-0
1 addition, 0 deletions
assets/optimize.mac.template
src/maxima_fork.c
+212
-0
212 additions, 0 deletions
src/maxima_fork.c
with
261 additions
and
14 deletions
Dockerfile
+
19
−
14
View file @
fafb4ad0
...
@@ -4,11 +4,8 @@ FROM debian:stable
...
@@ -4,11 +4,8 @@ FROM debian:stable
ARG
MAXIMA_VERSION
ARG
MAXIMA_VERSION
# e.g. 2.0.22.0.2
# e.g. 2.0.22.0.2
ARG
SBCL_VERSION
ARG
SBCL_VERSION
# e.g. assStackQuestion/classes/stack/maxima
ARG
LIB_PATH
RUN
echo
${
LIB_PATH
?Error \
$LIB_PATH
is not defined
}
\
RUN
echo
${
MAXIMA_VERSION
?Error \
$MAXIMA_VERSION
is not defined
}
\
${
MAXIMA_VERSION
?Error \
$MAXIMA_VERSION
is not defined
}
\
${
SBCL_VERSION
?Error \
$SBCL_VERSION
is not defined
}
${
SBCL_VERSION
?Error \
$SBCL_VERSION
is not defined
}
ENV
SRC=/opt/src \
ENV
SRC=/opt/src \
...
@@ -28,6 +25,7 @@ RUN apt-get update \
...
@@ -28,6 +25,7 @@ RUN apt-get update \
make
\
make
\
wget
\
wget
\
python3
\
python3
\
gcc
\
# ca-certificates
\
# ca-certificates
\
# curl
\
# curl
\
texinfo
texinfo
...
@@ -55,16 +53,28 @@ RUN cd ${SRC} \
...
@@ -55,16 +53,28 @@ RUN cd ${SRC} \
&&
make
install
\
&&
make
install
\
&&
make clean
&&
make clean
RUN
apt-get
install
-y
gnuplot gettext-base
sudo
psmisc libbsd-dev tini
COPY
./src/maxima_fork.c ${SRC}
RUN
cd
${
SRC
}
&&
gcc
-shared
maxima_fork.c
-lbsd
-fPIC
-Wall
-Wextra
-o
libmaximafork.so
\
&&
mv
libmaximafork.so /usr/lib
RUN
rm
-r
${
SRC
}
/SBCL_ARCH
RUN
rm
-r
${
SRC
}
/SBCL_ARCH
RUN
apt-get
install
-y
gnuplot gettext-base
sudo
psmisc
RUN
mkdir
-p
${
LIB
}
${
LOG
}
${
TMP
}
${
PLOT
}
${
ASSETS
}
${
BIN
}
RUN
mkdir
-p
${
LIB
}
${
LOG
}
${
TMP
}
${
PLOT
}
${
ASSETS
}
${
BIN
}
# e.g. assStackQuestion/classes/stack/maxima
ARG
LIB_PATH
RUN
echo
${
LIB_PATH
?Error \
$LIB_PATH
is not defined
}
# Copy Libraries
# Copy Libraries
COPY
${LIB_PATH} ${LIB}
COPY
${LIB_PATH} ${LIB}
# Copy optimization scripts
# Copy optimization scripts
COPY
assets/optimize.mac.template assets/maximalocal.mac.template ${ASSETS}/
COPY
assets/maxima-fork.lisp
assets/optimize.mac.template assets/maximalocal.mac.template ${ASSETS}/
RUN
grep
stackmaximaversion
${
LIB
}
/stackmaxima.mac |
grep
-oP
"
\d
+"
>>
/opt/maxima/stackmaximaversion
\
RUN
grep
stackmaximaversion
${
LIB
}
/stackmaxima.mac |
grep
-oP
"
\d
+"
>>
/opt/maxima/stackmaximaversion
\
&&
sh
-c
'envsubst < ${ASSETS}/maximalocal.mac.template > ${ASSETS}/maximalocal.mac
\
&&
sh
-c
'envsubst < ${ASSETS}/maximalocal.mac.template > ${ASSETS}/maximalocal.mac
\
...
@@ -74,19 +84,14 @@ RUN grep stackmaximaversion ${LIB}/stackmaxima.mac | grep -oP "\d+" >> /opt/maxi
...
@@ -74,19 +84,14 @@ RUN grep stackmaximaversion ${LIB}/stackmaxima.mac | grep -oP "\d+" >> /opt/maxi
&&
maxima
-b
optimize.mac
\
&&
maxima
-b
optimize.mac
\
&&
mv
maxima-optimised
${
BIN
}
/maxima-optimised
&&
mv
maxima-optimised
${
BIN
}
/maxima-optimised
RUN
apt-get purge
-y
wget python3 make bzip2 texinfo
RUN
apt-get purge
-y
wget python3 make bzip2 texinfo
gcc
RUN
useradd
-M
maxima-server
&&
echo
"Defaults lecture = always"
>
/etc/sudoers.d/maxima
RUN for
i
in
$(
seq
16
)
;
do
\
RUN for
i
in
$(
seq
16
)
;
do
\
useradd
-M
"maxima-
$i
"
\
useradd
-M
"maxima-
$i
"
;
\
&&
echo
"maxima-server ALL = (maxima-
$i
) NOPASSWD:
${
BIN
}
/wrapper"
>>
/etc/sudoers.d/maxima
\
&&
echo
"maxima-server ALL = (root) NOPASSWD: /usr/bin/killall -9 -u maxima-
$i
"
>>
/etc/sudoers.d/maxima
;
\
done
done
# Add go webserver
# Add go webserver
COPY
./bin/web ${BIN}/goweb
COPY
./bin/web ${BIN}/goweb
# Add wrapper
COPY
./bin/wrapper ${BIN}/wrapper
CMD
["su", "-c", "/opt/maxima/bin/goweb", "maxima-server"]
CMD
rm /dev/tty && exec tini ${BIN}/goweb
This diff is collapsed.
Click to expand it.
assets/maxima-fork.lisp
0 → 100644
+
29
−
0
View file @
fafb4ad0
(
cl:in-package
"MAXIMA"
)
(
defun
set-tmp-dir-vars
(
tmp-dir
)
(
defparameter
|$image_dir|
(
concatenate
'string
tmp-dir
"/output/"
))
(
defparameter
|$MAXIMA_TEMPDIR|
(
concatenate
'string
tmp-dir
"/work/"
))
nil
)
(
defparameter
|$url_base|
"!ploturl!"
)
(
cl:defpackage
"MAXIMA-FORK"
(
:use
"CL"
"SB-ALIEN"
)
(
:export
"FORKING-LOOP"
))
(
cl:in-package
"MAXIMA-FORK"
)
;;; load shared library
(
load-shared-object
"libmaximafork.so"
)
;;; define c function
(
declaim
(
inline
fork-new-process
))
(
define-alien-routine
fork-new-process
c-string
)
;;; forking loop
(
defun
forking-loop
()
(
finish-output
)
(
let
((
tmp-dir
(
fork-new-process
)))
(
when
(
not
tmp-dir
)
(
sb-ext:exit
:code
1
))
(
maxima::set-tmp-dir-vars
tmp-dir
))
t
)
This diff is collapsed.
Click to expand it.
assets/optimize.mac.template
+
1
−
0
View file @
fafb4ad0
...
@@ -3,6 +3,7 @@
...
@@ -3,6 +3,7 @@
https://github.com/maths/moodle-qtype_stack/blob/master/doc/en/CAS/Optimising_Maxima.md */
https://github.com/maths/moodle-qtype_stack/blob/master/doc/en/CAS/Optimising_Maxima.md */
load("${ASSETS}/maximalocal.mac");
load("${ASSETS}/maximalocal.mac");
load("${ASSETS}/maxima-fork.lisp");
load("${LIB}/stackmaxima.mac");
load("${LIB}/stackmaxima.mac");
load(stats);
load(stats);
load(distrib);
load(distrib);
...
...
This diff is collapsed.
Click to expand it.
src/maxima_fork.c
0 → 100644
+
212
−
0
View file @
fafb4ad0
#include
<stdio.h>
#include
<string.h>
#include
<errno.h>
#include
<stdlib.h>
#include
<pwd.h>
#include
<signal.h>
#include
<sys/resource.h>
#include
<sys/types.h>
#include
<sys/stat.h>
#include
<sys/wait.h>
#include
<fcntl.h>
#include
<bsd/unistd.h>
#include
<limits.h>
#include
<grp.h>
#define N_SLOT 16
#define RNOFILE 256
#define FILEPATH_LEN (PATH_MAX + 1)
char
filepath
[
FILEPATH_LEN
];
// inits a maxima process for web service:
// changes gid/uid to maxima-{slot}
// redirects input/output, creates temporary subdirectories
char
*
fork_new_process
()
{
fflush
(
stdout
);
// send an S for Synchronization, so that
// the server process doesn't accidentally write into
// sbcl's buffer
// the server should not write anything before it has read this
write
(
STDOUT_FILENO
,
"S"
,
1
);
// while the loop is running, the SIGCHLD handler
// is deactivated so that children are automatically reaped
// after that, it is again restored
struct
sigaction
old
,
new
;
new
.
sa_handler
=
SIG_IGN
;
sigemptyset
(
&
new
.
sa_mask
);
new
.
sa_flags
=
SA_NOCLDWAIT
;
char
*
ret
=
NULL
;
if
(
sigaction
(
SIGCHLD
,
&
new
,
&
old
)
==
-
1
)
{
perror
(
"Could not set signal error for children"
);
return
NULL
;
}
// when sbcl spawns a child process through lisp, sbcl tries to close all
// filedescriptors until RLIMIT_NOFILE
// in docker containers, this is by standard quite high, so it takes long
// which is remediated here by setting it lower manually
struct
rlimit
nofile
=
{
.
rlim_cur
=
RNOFILE
,
.
rlim_max
=
RNOFILE
};
if
(
setrlimit
(
RLIMIT_NOFILE
,
&
nofile
)
==
-
1
)
{
perror
(
"Error setting rlimit_nofile"
);
sigaction
(
SIGCHLD
,
&
old
,
NULL
);
return
NULL
;
}
for
(;;)
{
// can't flush enough
fflush
(
stdout
);
int
slot
;
// the slot number and temp directory is sent to the process
// over stdin in the format "%d%s", where %s can contain anything
// but newlines and musn't start with a number, which isn't a
// problem for absolute paths
if
(
scanf
(
"%d"
,
&
slot
)
==
EOF
)
{
if
(
errno
!=
0
)
{
perror
(
"Error getting slot number from stdin"
);
ret
=
NULL
;
}
break
;
}
char
*
tempdir
=
fgets
(
filepath
,
FILEPATH_LEN
,
stdin
);
if
(
!
tempdir
)
{
perror
(
"Error getting temp path name"
);
ret
=
NULL
;
break
;
}
// remove the last newline, if it exists
size_t
last_char
=
strlen
(
tempdir
)
-
1
;
if
(
tempdir
[
last_char
]
==
'\n'
)
{
tempdir
[
strlen
(
tempdir
)
-
1
]
=
'\0'
;
}
// we fork the main process and use the child without execve
// this way, startup time is improved
pid_t
pid
=
fork
();
if
(
pid
==
-
1
)
{
perror
(
"Could not fork"
);
ret
=
NULL
;
break
;
}
if
(
pid
!=
0
)
{
continue
;
}
if
(
chdir
(
tempdir
)
==
-
1
)
{
perror
(
"Could not chdir to temporary directory"
);
ret
=
NULL
;
break
;
}
// redirect stdout to pipe
// note: open outpipe before inpipe to avoid deadlock
int
outfd
=
open
(
"outpipe"
,
O_WRONLY
);
if
(
outfd
==
-
1
)
{
perror
(
"Could not connect output pipe"
);
ret
=
NULL
;
break
;
}
if
(
dup2
(
outfd
,
STDOUT_FILENO
)
==
-
1
)
{
perror
(
"Could not copy output file descriptor"
);
ret
=
NULL
;
break
;
}
// redirect stdin from pipe
int
infd
=
open
(
"inpipe"
,
O_RDONLY
);
if
(
infd
==
-
1
)
{
perror
(
"Could not create input pipe"
);
ret
=
NULL
;
break
;
}
if
(
dup2
(
infd
,
STDIN_FILENO
)
==
-
1
)
{
perror
(
"Could not copy input file descriptor"
);
ret
=
NULL
;
break
;
}
// replace stdin with a new stream, for good measure
FILE
*
new_stdin
=
fdopen
(
STDIN_FILENO
,
"r"
);
if
(
!
new_stdin
)
{
perror
(
"Could not create stream from stdin"
);
ret
=
NULL
;
break
;
}
stdin
=
new_stdin
;
// everything execpt std{in,out,err} is closed
// note: this is a function from libbsd
closefrom
(
3
);
// get uid/gid from username
if
(
slot
<=
0
||
slot
>
N_SLOT
)
{
dprintf
(
STDERR_FILENO
,
"Invalid slot number: %d
\n
"
,
slot
);
ret
=
NULL
;
break
;
}
char
username
[
16
];
int
len
=
snprintf
(
username
,
15
,
"maxima-%d"
,
slot
);
if
(
len
<
0
||
len
>
15
)
{
dprintf
(
STDERR_FILENO
,
"Internal error getting user name
\n
"
);
ret
=
NULL
;
break
;
}
struct
passwd
*
userinfo
=
getpwnam
(
username
);
if
(
!
userinfo
)
{
dprintf
(
STDERR_FILENO
,
"Could not read user information for %s: %s
\n
"
,
username
,
strerror
(
errno
));
ret
=
NULL
;
break
;
}
uid_t
uid
=
userinfo
->
pw_uid
;
gid_t
gid
=
userinfo
->
pw_gid
;
if
(
uid
==
0
||
gid
==
0
)
{
dprintf
(
STDERR_FILENO
,
"Refusing to setuid/gid to root
\n
"
);
ret
=
NULL
;
break
;
}
// note: setgid should be executed before setuid when dropping from root
if
(
setgid
(
gid
)
==
-
1
)
{
perror
(
"Could not set gid"
);
ret
=
NULL
;
break
;
}
// remove all aux groups
if
(
setgroups
(
0
,
NULL
))
{
perror
(
"Could not remove aux groups"
);
ret
=
NULL
;
break
;
}
// after this, we should be non-root
if
(
setuid
(
uid
)
==
-
1
)
{
perror
(
"Could not set uid"
);
ret
=
NULL
;
break
;
}
// create temporary folders and files
if
(
mkdir
(
"output"
,
0770
)
==
-
1
)
{
perror
(
"Could not create output directory"
);
ret
=
NULL
;
break
;
}
if
(
mkdir
(
"work"
,
0770
)
==
-
1
)
{
perror
(
"Could not create work directory"
);
ret
=
NULL
;
break
;
}
ret
=
tempdir
;
break
;
}
// restore normal SIGCHLD handler
if
(
sigaction
(
SIGCHLD
,
&
old
,
NULL
)
==
-
1
)
{
return
NULL
;
}
return
ret
;
}
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment