From 7ca4fc3dbe2a5cf8bf5e98fe6f19342829024dde Mon Sep 17 00:00:00 2001 From: zii Date: Sat, 23 Dec 2023 17:38:11 -0500 Subject: [PATCH] v3.3.t9 -moved buff_path setup code away from rdconf() to loadSh(). this insures root will never take ownership of the camera buffer. -added service group option. -updated the documentation in preperation of release. --- README.md | 95 ++++++++++++++++++++++++++++++++---------------- install.sh | 4 ++ src/common.cpp | 35 ++++-------------- src/common.h | 5 +-- src/services.cpp | 21 ++++++----- src/services.h | 2 +- 6 files changed, 91 insertions(+), 71 deletions(-) diff --git a/README.md b/README.md index f0a9af1..8f56492 100644 --- a/README.md +++ b/README.md @@ -30,30 +30,34 @@ parameters supported and descriptions of each. # also note to avoid using empty lines. if you're going to need an empty # line, start it with a '#' # -recording_stream = rtsp://1.2.3.4:554/h264 -# this is the url to the main stream of the IP camera that will be used -# to record footage. +recording_uri = rtsp://1.2.3.4:554/h264 +# this is the uri to the main stream of the IP camera that will be used +# to record footage. it can be a url to an rtsp stream or a direct device +# path such as /dev/video0. # -web_root = /var/opt/mow/web -# this is the output directory that will be used to store recorded footage -# from the cameras as well as the web interface for the application. -# warning: this will overwrite any existing index.html files so be sure -# to choose a directory that doesn't have an existing website. -# -buffer_path = /var/opt/mow/buf +buffer_path = /var/buffer # this is the work directory the app will use to store live footage and # image frames. it's recommended to use a ram disk for this since there -# will be large amounts of io occuring here. 1g of space per camera is +# will be large amounts of io occuring here. 1GB of space per camera is # a good rule of thumb. # +rec_path = /var/footage +# this is video output directory that will be used to store any footage +# that contain any motion events. +# +live_secs = 30 +# this is the maximum amount of seconds worth of live footage to keep in +# buffer_path before deleting the oldest 2 seconds worth of footage. +# note: each video clip in buffer_path should be 2 seconds long. +# cam_name = cam-1 # this is the optional camera name parameter to identify the camera. this -# name will also be used to as the base directory in web_root. if not -# defined, the name of the config file will be used. +# name will also be used to as the base directory in buffer_path or +# rec_path. if not defined, the name of the config file will be used. # max_event_secs = 30 -# this is the maximum amount of secs of video footage that can be -# recorded in a motion event. +# this is the maximum amount of secs of video footage that can be recorded +# in a motion event. # img_comp_cmd = magick compare -metric FUZZ &prev& &next& /dev/null # this is the command line template this application will use when calling @@ -74,14 +78,35 @@ img_comp_out = stderr # use to output the comparison score. this can only be stderr or stdout, # any other stream name is considered invalid. # +stream_codec = copy +# this is the encoding codec to use when recording footage from the camera. +# the list of supported codecs entirely depend on the hosts' ffmpeg +# installation. run 'ffmpeg -codecs' to determine this list. if not +# defined, 'copy' will be used as in it will just copy the codec format +# from camera itself. +# +stream_ext = .avi +# this is the file extension that will be used to when recording footage +# from the camera in buffer_path. ffmpeg will also use this to determine +# what format container to use for the video clips. +# +thumbnail_ext = .jpg +# this the image format that will be used when creating the thumbnails +# for the videos clips recorded to rec_path. +# +rec_ext = .avi +# this the the file extension this will be used when storaging motion +# footage to rec_path. ffmpeg will also use this to determine what format +# container to use for the video clips. +# img_thresh = 8000 # this parameter defines the score threshold from img_comp_cmd that will # be considered motion. any motion events will queue up max_event_secs -# worth of hls clips to be written out to web_root. +# worth of video clips to be written out to rec_path. # max_events = 100 # this indicates the maximum amount of motion event video clips to keep -# before deleting the oldest clip. +# in rec_path before deleting the oldest clip. # post_secs = 60 # this is the amount of seconds to wait before running the command @@ -93,23 +118,31 @@ post_cmd = move_the_ptz_camera.py # is to move a ptz camera to the next position of it's patrol pattern. # note: the call to this command will be delayed if motion was detected. # -max_log_size = 50000 -# this is the maximum byte size of all log files that can be stored in -# web_root. whenever this limit is met, the log file will be deleted and -# then eventually recreated blank. +rec_fps = 30 +# this sets the recording frames per second for the footage recorded +# from the camera. this has no affect of using 'copy' as the +# stream_codec. # -web_text = #dee5ee -# this can be used to customize the color of the text in the web -# interface. it can be defined as any color understood by html5 standard. +rec_scale = 1280:720 +# this sets the pixel scale of the recorded footage from the camera. it +# uses width, height numeric strings seperated by a colon, eg W:H. this +# has no affect of using 'copy' as the stream_codec. # -web_bg = #485564 -# this can be used to customize the background color of the web -# interface. just like web_text, it also follows the html5 standard. +img_scale = 320:240 +# this sets the pixel size of the thumbnails for recorded stored in +# rec_path. it uses width, height numeric strings seperated by a colon, +# eg W:H. # -web_font = courier -# this will customize the text font family to use in the web interface. -# it is recommended to use mono-spaced font because this is also used to -# display logs and logs are best displayed in mono-spaced font. +service_user = mow +# this sets the service local user of the application dictating the +# amount privilege it will have on the host. if not defined, the +# unprivileged user 'mow' will be used. which ever user is defined here +# just make sure it has read/write access to buffer_path and rec_path. +# +service_group = mow +# this sets the service local group of the application that can further +# refine host privileges. if not defined, the name stored in +# service_user will be used. ``` ### Setup/Build/Install ### diff --git a/install.sh b/install.sh index af4b829..f222afd 100644 --- a/install.sh +++ b/install.sh @@ -3,6 +3,10 @@ if [ -f "/opt/mow/uninst" ]; then mow -u -f fi +if [ ! -d "/opt" ]; then + mkdir /opt +fi + if [ ! -d "/opt/mow" ]; then mkdir /opt/mow fi diff --git a/src/common.cpp b/src/common.cpp index a950d12..3cc5993 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -212,6 +212,7 @@ bool rdConf(const QString &filePath, shared_t *share) share->camName.clear(); share->buffPath.clear(); share->recPath.clear(); + share->servGroup.clear(); auto thrCount = QThread::idealThreadCount() / 2; @@ -228,8 +229,6 @@ bool rdConf(const QString &filePath, shared_t *share) share->streamExt = ".avi"; share->recExt = ".avi"; share->thumbExt = ".jpg"; - share->imgThreads = thrCount; - share->recThreads = thrCount; share->recFps = 30; share->liveSecs = 80; share->recScale = "1280:720"; @@ -259,12 +258,11 @@ bool rdConf(const QString &filePath, shared_t *share) rdLine("stream_ext = ", line, &share->streamExt); rdLine("rec_ext = ", line, &share->recExt); rdLine("thumbnail_ext = ", line, &share->thumbExt); - rdLine("img_threads = ", line, &share->imgThreads); - rdLine("rec_threads = ", line, &share->recThreads); rdLine("rec_fps = ", line, &share->recFps); rdLine("rec_scale = ", line, &share->recScale); rdLine("img_scale = ", line, &share->imgScale); rdLine("service_user = ", line, &share->servUser); + rdLine("service_group = ", line, &share->servGroup); rdLine("live_secs = ", line, &share->liveSecs); } @@ -311,31 +309,14 @@ bool rdConf(const QString &filePath, shared_t *share) { share->evMaxSecs = share->liveSecs - 4; } - - share->retCode = EACCES; - - if (!mkPath(share->recPath)) + + if (share->servGroup.isEmpty()) { - QTextStream(stderr) << "err: failed to create recording directory - " << share->recPath << " check for write permissions." << Qt::endl; - } - else if (!mkPath(share->buffPath)) - { - QTextStream(stderr) << "err: failed to create buffer directory - " << share->buffPath << " check for write permissions." << Qt::endl; - } - else if (!mkPath(share->buffPath + "/img")) - { - QTextStream(stderr) << "err: failed to create 'img' in the buffer directory - " << share->buffPath << "/img" << " check for write permissions." << Qt::endl; - } - else if (!mkPath(share->buffPath + "/live")) - { - QTextStream(stderr) << "err: failed to create 'live' in the buffer directory - " << share->buffPath << "/live" << " check for write permissions." << Qt::endl; - } - else - { - share->retCode = 0; - share->servMainLoop = QString(APP_BIN) + ".main_loop." + share->camName; - share->servVidLoop = QString(APP_BIN) + ".vid_loop." + share->camName; + share->servGroup = share->servUser; } + + share->servMainLoop = QString(APP_BIN) + ".main_loop." + share->camName; + share->servVidLoop = QString(APP_BIN) + ".vid_loop." + share->camName; } return share->retCode == 0; diff --git a/src/common.h b/src/common.h index 64644f0..5c9c5f7 100644 --- a/src/common.h +++ b/src/common.h @@ -30,7 +30,7 @@ using namespace std; -#define APP_VER "3.3.t8" +#define APP_VER "3.3.t9" #define APP_NAME "Motion Watch" #define APP_BIN "mow" #define DATETIME_FMT "yyyyMMddhhmmss" @@ -74,12 +74,11 @@ struct shared_t QString servMainLoop; QString servVidLoop; QString servUser; + QString servGroup; bool singleTenant; bool skipCmd; int liveSecs; int recFps; - int imgThreads; - int recThreads; int evMaxSecs; int postSecs; int imgThresh; diff --git a/src/services.cpp b/src/services.cpp index d1251b7..ec5d14b 100644 --- a/src/services.cpp +++ b/src/services.cpp @@ -64,7 +64,7 @@ int loadService(const QString &desc, const QString &user, const QString &servNam return ret; } -int loadSh(const QString &name, const QString &exeCmd, const QString &workDir) +int loadSh(const QString &name, const QString &exeCmd, const QString &buffDir, const QString &outDir, const QString &servUser, const QString &servGroup) { QFile file("/usr/bin/" + name); @@ -79,11 +79,18 @@ int loadSh(const QString &name, const QString &exeCmd, const QString &workDir) else { file.write("#!/bin/sh\n\n"); - file.write("cd " + workDir.toUtf8() + "\n"); + file.write("cd " + buffDir.toUtf8() + "\n"); file.write(exeCmd.toUtf8() + "\n"); file.close(); + + mkPath(buffDir); + mkPath(outDir); + mkPath(buffDir + "/live"); + mkPath(buffDir + "/img"); QProcess::execute("chmod", {"-v", "+x", file.fileName()}); + QProcess::execute("chown", {servUser + ":" + servGroup, "-R", buffDir}); + QProcess::execute("chown", {servUser + ":" + servGroup, "-R", outDir}); } file.close(); @@ -135,8 +142,8 @@ int loadServiceByConf(const QString &confFile, bool start) { auto desc = QString(APP_NAME) + " - " + conf.camName; - if (ret == 0) ret = loadSh(conf.servMainLoop, camCmdFromConf(&conf, MAIN_LOOP), conf.buffPath); - if (ret == 0) ret = loadSh(conf.servVidLoop, camCmdFromConf(&conf, VID_LOOP), conf.buffPath); + if (ret == 0) ret = loadSh(conf.servMainLoop, camCmdFromConf(&conf, MAIN_LOOP), conf.buffPath, conf.recPath, conf.servUser, conf.servGroup); + if (ret == 0) ret = loadSh(conf.servVidLoop, camCmdFromConf(&conf, VID_LOOP), conf.buffPath, conf.recPath, conf.servUser, conf.servGroup); if (ret == 0) ret = loadService(desc, conf.servUser, conf.servMainLoop, conf.buffPath, conf.recPath, start); if (ret == 0) ret = loadService(desc, conf.servUser, conf.servVidLoop, conf.buffPath, conf.recPath, start); @@ -171,11 +178,7 @@ int rmServiceByConf(const QString &confFile) conf.retCode = rmService(conf.servMainLoop); conf.retCode = rmService(conf.servVidLoop); - QDir live(conf.buffPath + "/live"); - QDir img(conf.buffPath + "/img"); - - live.removeRecursively(); - img.removeRecursively(); + QDir(conf.buffPath).removeRecursively(); } return conf.retCode; diff --git a/src/services.h b/src/services.h index 576927b..f310e5d 100644 --- a/src/services.h +++ b/src/services.h @@ -24,6 +24,6 @@ int loadServiceByDir(const QString &path, bool start); int rmServiceByConf(const QString &confFile); int rmService(const QString &servName); int rmServiceByDir(const QString &path); -int loadSh(const QString &name, const QString &exeCmd, const QString &workDir); +int loadSh(const QString &name, const QString &exeCmd, const QString &buffDir, const QString &outDir, const QString &servUser, const QString &servGroup); #endif // SERVICES_H