diff --git a/README.md b/README.md index 8a6173a..edf10ce 100644 --- a/README.md +++ b/README.md @@ -1,74 +1,60 @@ -Updated May 24, 2023. + +Updated June 24, 2023. + Want to support this work? Buy Me a Coffee. https://www.buymeacoffee.com/ppaukstelis. Need help with octoprint_deploy? Ask on Discord: https://discord.gg/6vgSjgvR6u -# octoprint_deploy -These files provide a bash script for quickly deploying multiple octoprint instances on a single computer. For Linux systems (Ubuntu, Fedora, etc.) it will also install OctoPrint and a video streamer (mjpg-streamer or ustreamer). No need for lots of file editing or complicated Docker compose scripts! A background video on how it works from my ERRF2022 talk can be found here: https://www.youtube.com/watch?v=q0iCNl8-kJI&t=15378s - +# octoprint_deploy 1.0.0 - ALL NEW +* These files provide a bash script for quickly deploying multiple octoprint instances on a single computer. For Linux systems (Ubuntu, Fedora, etc.) it will also install OctoPrint and a video streamer (ustreamer). No need for lots of file editing or complicated Docker compose scripts! A background video on how it generally works from my ERRF2022 talk can be found here: https://www.youtube.com/watch?v=q0iCNl8-kJI&t=15378s +* octoprint_deploy and octoprint_install are being merged! Maintaining two separate scripts was close to twice the amount of work. By merging the scripts many new features have been included, while also providing greater simplicity in setup. For now, the merged script will be called octoprint_deploy and at some point the octoprint_install repository will point here. +* The biggest change is that there is no longer the notion of a single 'template' OctoPrint instance. Now, _any_ previously configured instance can be used as a template when a new instance is created. The choice is up to the user. +* Unfortunately, octoprint_deploy > 1.0.0 is not directly compatible with older versions, as so much has changed. If you want to use the new version of octoprint_deploy with an older setup, create backups (either with OctoPrint UI or with octoprint_deploy), then use the `remove` commandline argument before updating octoprint_deploy. Re-make your instances using the same instance names, then recover your backups. # How to use * OctoPi - * Slightly dated YouTube video for OctoPi setup here: https://www.youtube.com/watch?v=J5VzI4AFav4&lc - * Put the latest OctoPi image on your SD card. + * OctoPi is intended as a single printer environment. This script makes adding multiple instances easy, but it cannot take into account everything OctoPi does (mostly surrounding cameras). You have been warned. * ssh into your Pi (pi@octopi.local; good idea to change your password now!). - * run the command `git clone https://github.com/paukstelis/octoprint_deploy.git`. + * run the command `git clone https://github.com/paukstelis/octoprint_deploy`. * run the command `sudo octoprint_deploy/octoprint_deploy.sh`. * Choose `Prepare System` from the menu. - * If you have already been using this system for some time, you will be prompted that files will be moved in order to generate a template instance. - * You will be prompted if you want to switch from mjpeg-streamer to ustreamer. - * You will be prompted if you want to setup the admin user and do the first run wizard using the commandline. If you do this now you can start making new instances when the system preparation is complete. - * You will be prompted if you want to install recommended plugins and cloud plugins. This can be useful if you want to configure plugins in your template instance, before adding new instances. - * If you did not setup admin user in the script, setup admin user by connecting to http://octopi.local via browser. - * Back in the ssh session, choose `New Instance` and follow the instructions. - * Do not plug your printer in by USB until you are asked to do so. + * This will register the OctoPi-created instance in octoprint_deploy. + * You will be prompted for udev detection (only needed if you are adding multiple printers). + * You will be prompted for installing a new streamer. This will be the default streamer for any _additional_ cameras that are installed. + * To add more printers choose `Add Instance` and follow the instructions. + * You will be asked if you want to use an existing instance as a template. This will copy all files from this existing instance to your new instance. * If your printer does not have a serial number (all Creality printers), it will detect and use the physical USB address for udev entries. * Continue until you have added all the printers you want to use. * haproxy entries are updated so you can connect via http://octopi.local/instancename * To add more printers at a later date, just run the script again! * Want to use a Pi camera? After you have made your instance(s), run the script with `sudo octoprint_deploy/octoprint_deploy.sh picam` and follow the instructions (VERY EXPERIMENTAL). * General Linux (Ubuntu/Mint/RPiOS/Debian/Fedora/Arch/etc.) - * __You do not need to install OctoPrint using any Wiki instructions, snap, etc. The script will do it for you.__ + * __You do not need to install OctoPrint using any Wiki instructions, snap, etc. The script will do it all for you.__ * octoprint_deploy uses systemd services, so avoid distros that do not use systemd by default (MX Linux or chroot based systems like Chrome+crouton) - * SELinux (Fedora) casues issues, particularly with camera services. Use at your own risk (or disable SELinux). - * Basic guide video here: https://youtu.be/1YINWQ5fNn0 * All commands assume you are operating out of your home directory. - * Install Ubuntu 20+, Mint 20.3+, Debian, DietPi, RPiOS, Armbian, Fedora35+, or ArchLinux on your system (make sure your user is admin for sudo). - * Install git if it isn't already: `sudo apt install git` or `sudo dnf install git` or `sudo pacman -S git`. - * run the command `git clone https://github.com/paukstelis/octoprint_deploy.git`. + * Install Ubuntu 20+, Mint 20.3+, Debian, DietPi, RPiOS, Armbian, Fedora35+, ArchLinux, or openSUSE on your system (make sure your user is admin for sudo). + * Install git if it isn't already: `sudo apt install git` or `sudo dnf install git` or `sudo pacman -S git` or `sudo zypper in git`. + * run the command `git clone https://github.com/paukstelis/octoprint_deploy`. * run the command `sudo octoprint_deploy/octoprint_deploy.sh`. - * Choose `Prepare System` from the menu. Select your distribution type. All deb-based system use the same selection. This will install necessary packages, install OctoPrint, and start a template instance. + * Choose `Prepare System` from the menu. Select your distribution type. All deb-based system use the same selection. This will install necessary packages, install OctoPrint, and prompt you to create the first instance. * You will be asked if you want to use haproxy. This will make your instances available on port 80 e.g. http://hostname.local/instancename/ - * You will be asked which streamer you would like to install (mjpg-streamer or ustreamer). - * You will be prompted if you want to setup the admin user and do the first run wizard via the commandline. If you do this now you can start making new instances as soon as the system preparation is complete. - * You will be prompted if you want to install recommended plugins and cloud plugins. This can be useful if you want to configure plugins in your template instance, before adding new instances. - * If you didn't setup admin user in the step above, setup admin user by connecting to your system (either http://localhost:5000 or http://[hostname]:5000 via a browser - * __This instance is just a generic template used for making all your other instances. You need to make at least one instance using the script when this is done.__ - * Continue with octoprint_deploy script, choose `New Instance` and follow the instructions. - * Do not plug the printer in by USB until you are asked to do so. + * You will be asked which streamer you would like to install (ustreamer, mjpg-streamer or camera-streamer). Please note, not all distributions will be compatible with camera-streamer. + * You will be prompted if you want to setup the admin user and do the first run wizard via the commandline. + * You will be prompted if you want to install recommended plugins. + * Continue with octoprint_deploy script, choose `Add Instance` and follow the instructions. * If your printer does not have a serial number (all Creality printers) it will be detected by the USB port you plugged it in to. - * After adding the first instance, the template instance will be shutdown. This is normal. + * You can also setup a camera for the instance at this time. Follow the instructions. * Add as many instances as you have printers, following the instructions. - * To add more printers at a later date, or to add cameras to an instance, simply run the script again (`sudo octoprint_deploy/octoprint_deploy.sh`) and choose the appropriate options. - * Remember, camera installed with this script are experimental and always will be. The script makes some basic assumptions that you may need to change later. Cameras suck up quite a bit of USB bandwidth so while it is quite straightforward to run 10 printers with a modest computer, you can't also run 10 cameras. -* What else can you do? - * Remove instances - * Add USB webcams AFTER you've created the instance - * Test USB connections - * Write udev rules without deploying instances (udev_rules.sh) + * To add more printers at a later date, or to add cameras to an instance later, simply run the script again (`sudo octoprint_deploy/octoprint_deploy.sh`) and choose the appropriate option. +* Other features from commandline arguments * Want to get rid of everything? `sudo octoprint_deploy/octoprint_deploy.sh remove` * Backup and restore files for an instance from the menu, or backup all instances with `sudo octoprint_deploy/octoprint_deploy backup` - * Restart all non-template instances from the command line: `sudo octoprint_deploy/octoprint_deploy.sh restart_all` - * Change udev rules for an instance with `sudo octoprint_deploy/octoprint_deploy.sh replace` - * Always a good idea to update octoprint_deploy from time-to-time with `git -C octoprint_deploy pull` + * Restart all instances from the command line: `sudo octoprint_deploy/octoprint_deploy.sh restart_all` # Recent Changes -* Adjustments for OctoPrint 1.9.x with classic webcam now as a plugin. -* Remove cloud plugin setup for template. This causes more issues than it helps. Cloud plugins should typically be installed manually and configured for each instance. -* Verify that OctoPrint gets installed in virtual environment. Opens remove_everything prompt of executable does not exist. -* Remove path double slashes that seemed to prevented deletion of timelapse videos. -* Check for spaces in instance name, admin user, and password -* Force camera port to be greater than 7000 -* Allow removal of individual camera services. Improvements to instance and camera removal. -* Haproxy fixed! No more trailing slash required! Running new octoprint_deploy on an older installation automatically update these entries. -* Multi-camera support (experimental). Clean-up of haproxy.cfg on instance removal still needs work. -* If haproxy is used, cameras stream can be placed behind it. PLEASE NOTE: if cameras are used with haproxy a relative stream path is used. This means that your stream will not show up in the Control tab unless you access with the haproxy path (http://host/instancename/) (remember, trailing slash is required!) + * Lots of changes, now octoprint_deploy 1.0.0 + * Udev utility menu + * Diagnostic information from menu provides a variety of useful information about the system. + * Cameras have additional fallback detection (/dev/v4l/by-id entries) +# TODO + * Integration with OctoPi new camera stack. This may or may not happen. + * Detection of existing instances/binaries that can be used instead of a full install (preserves plugins) diff --git a/cameras.sh b/cameras.sh new file mode 100755 index 0000000..ea12af0 --- /dev/null +++ b/cameras.sh @@ -0,0 +1,281 @@ +#!/bin/bash + + +detect_camera() { + echo + echo + echo "Verify the camera is currently unplugged from USB....." + if prompt_confirm "Is the camera you are trying to detect unplugged from USB?"; then + readarray -t c1 < <(ls -1 /dev/v4l/by-id/*index0 2>/dev/null) + fi + dmesg -C + echo "Plug your camera in via USB now (detection time-out in 1 min)" + counter=0 + while [[ -z "$CAM" ]] && [[ $counter -lt 60 ]]; do + CAM=$(dmesg | sed -n -e 's/^.*SerialNumber: //p') + TEMPUSBCAM=$(dmesg | sed -n -e 's|^.*input:.*/\(.*\)/input/input.*|\1|p') + counter=$(( $counter + 1 )) + if [[ -n "$TEMPUSBCAM" ]] && [[ -z "$CAM" ]]; then + break + fi + sleep 1 + done + readarray -t c2 < <(ls -1 /dev/v4l/by-id/*index0) + #https://stackoverflow.com/questions/2312762 + #TODO: what if there is more than one element? + BYIDCAM=(`echo ${c2[@]} ${c1[@]} | tr ' ' '\n' | sort | uniq -u `) + echo $BYIDCAM + dmesg -C +} + +remove_camera() { + systemctl stop $1.service + systemctl disable $1.service + rm /etc/systemd/system/$1.service + sed -i "/$1/d" /etc/udev/rules.d/99-octoprint.rules + sed -i "/$1/d" /etc/octoprint_cameras + if [ "$HAPROXY" == true ]; then + sed -i "/use_backend $1/d" /etc/haproxy/haproxy.cfg + sed -i "/#$1 start/,/#$1 stop/d" /etc/haproxy/haproxy.cfg + systemctl restart haproxy + fi +} + +write_camera() { + + get_settings + if [ -z "$STREAMER" ]; then + STREAMER="mjpg-streamer" + fi + + if [ -n "$BYIDCAM" ] && [ -z "$CAM" ] && [ -z "$TEMPUSBCAM" ]; then + CAMDEVICE=$BYIDCAM + else + CAMDEVICE=cam${INUM}_$INSTANCE + fi + + #mjpg-streamer + if [ "$STREAMER" == mjpg-streamer ]; then + cat $SCRIPTDIR/octocam_mjpg.service | \ + sed -e "s/OCTOUSER/$OCTOUSER/" \ + -e "s/OCTOCAM/$CAMDEVICE/" \ + -e "s/RESOLUTION/$RESOLUTION/" \ + -e "s/FRAMERATE/$FRAMERATE/" \ + -e "s/CAMPORT/$CAMPORT/" > $SCRIPTDIR/cam${INUM}_$INSTANCE.service + fi + + #ustreamer + if [ "$STREAMER" == ustreamer ]; then + cat $SCRIPTDIR/octocam_ustream.service | \ + sed -e "s/OCTOUSER/$OCTOUSER/" \ + -e "s/OCTOCAM/$CAMDEVICE/" \ + -e "s/RESOLUTION/$RESOLUTION/" \ + -e "s/FRAMERATE/$FRAMERATE/" \ + -e "s/CAMPORT/$CAMPORT/" > $SCRIPTDIR/cam${INUM}_$INSTANCE.service + fi + + mv $SCRIPTDIR/cam${INUM}_$INSTANCE.service /etc/systemd/system/ + echo "camera:cam${INUM}_$INSTANCE port:$CAMPORT udev:true" >> /etc/octoprint_cameras + + #config.yaml modifications - only if INUM not set + if [ -z "$INUM" ]; then + sudo -u $user $OCTOEXEC --basedir $BASE config set plugins.classicwebcam.snapshot "http://localhost:$CAMPORT?action=snapshot" + + if [ -z "$CAMHAPROXY" ]; then + sudo -u $user $OCTOEXEC --basedir $BASE config set plugins.classicwebcam.stream "http://$(hostname).local:$CAMPORT?action=stream" + else + sudo -u $user $OCTOEXEC --basedir $BASE config set plugins.classicwebcam.stream "/cam_$INSTANCE/?action=stream" + fi + + sudo -u $user $OCTOEXEC --basedir $BASE config append_value --json system.actions "{\"action\": \"Reset video streamer\", \"command\": \"sudo systemctl restart cam_$INSTANCE\", \"name\": \"Restart webcam\"}" + + if prompt_confirm "Instance must be restarted for settings to take effect. Restart now?"; then + systemctl restart $INSTANCE + fi + fi + + write_cam_udev + + if [ -n "$CAMHAPROXY" ]; then + HAversion=$(haproxy -v | sed -n 's/^.*version \([0-9]\).*/\1/p') + #find frontend line, do insert + sed -i "/use_backend $INSTANCE if/a\ use_backend cam${INUM}_$INSTANCE if { path_beg /cam${INUM}_$INSTANCE/ }" /etc/haproxy/haproxy.cfg + if [ $HAversion -gt 1 ]; then + EXTRACAM="backend cam${INUM}_$INSTANCE\n\ + http-request replace-path /cam${INUM}_$INSTANCE/(.*) /|\1\n\ + server webcam1 127.0.0.1:$CAMPORT" + else + EXTRACAM="backend cam${INUM}_$INSTANCE\n\ + reqrep ^([^|\ :]*)|\ /cam${INUM}_$INSTANCE/(.*) |\1|\ /|\2 \n\ + server webcam1 127.0.0.1:$CAMPORT" + fi + + echo "#cam${INUM}_$INSTANCE start" >> /etc/haproxy/haproxy.cfg + sed -i "/#cam${INUM}_$INSTANCE start/a $EXTRACAM" /etc/haproxy/haproxy.cfg + #these are necessary because sed append seems to have issues with escaping for the /\1 + sed -i 's/\/|1/\/\\1/' /etc/haproxy/haproxy.cfg + sed -i 's/\/|2/\/\\2/' /etc/haproxy/haproxy.cfg + #haproxy 1.x correction + sed -i 's/|/\\/g' /etc/haproxy/haproxy.cfg + echo "#cam${INUM}_$INSTANCE stop" >> /etc/haproxy/haproxy.cfg + + systemctl restart haproxy + fi +} + +write_cam_udev() { + #Either Serial number or USB port + #Serial Number + if [ -n "$CAM" ]; then + echo SUBSYSTEM==\"video4linux\", ATTRS{serial}==\"$CAM\", ATTR{index}==\"0\", SYMLINK+=\"cam${INUM}_$INSTANCE\" >> /etc/udev/rules.d/99-octoprint.rules + fi + + #USB port camera + if [ -n "$USBCAM" ]; then + echo SUBSYSTEM==\"video4linux\",KERNELS==\"$USBCAM\", SUBSYSTEMS==\"usb\", ATTR{index}==\"0\", DRIVERS==\"uvcvideo\", SYMLINK+=\"cam${INUM}_$INSTANCE\" >> /etc/udev/rules.d/99-octoprint.rules + fi +} + +add_camera() { + PI=$1 + INUM='' + CAMHAPROX='' + get_settings + if [ $SUDO_USER ]; then user=$SUDO_USER; fi + echo 'Adding camera' | log + if [ -z "$INSTANCE" ]; then + PS3='Select instance number to add camera to: ' + get_instances true + select camopt in "${INSTANCE_ARR[@]}" + do + if [ "$camopt" == Quit ]; then + main_menu + fi + echo "Selected instance for camera: ${cyan}$camopt${white}" + INSTANCE=$camopt + OCTOCONFIG="/home/$user/" + BASE="/home/$user/.$INSTANCE" + OCTOUSER=$user + if grep -q "cam_$INSTANCE" /etc/udev/rules.d/99-octoprint.rules; then + echo "It appears this instance already has at least one camera." + if prompt_confirm "Do you want to add an additional camera for this instance?"; then + echo "Enter a number for this camera." + echo "Ex. entering 2 will setup a service called cam2_$INSTANCE" + echo + read INUM + if [ -z "$INUM" ]; then + echo "No value given, setting as 2" + INUM='2' + fi + else + main_menu + fi + fi + break + done + fi + #for now just set a flag that we are going to write cameras behind haproxy + if [ "$HAPROXY" == true ]; then + if prompt_confirm "Add cameras to haproxy?"; then + CAMHAPROXY=1 + fi + fi + + if [ -z "$PI" ]; then + detect_camera + if [ -n "$NOSERIAL" ] && [ -n "$CAM" ]; then + unset CAM + fi + #Failed state. Nothing detected + if [ -z "$CAM" ] && [ -z "$TEMPUSBCAM" ] && [ -z "$BYIDCAM"]; then + echo + echo "${red}No camera was detected during the detection period.${white}" + echo "Try again or try a different camera." + + return + fi + #only BYIDCAM + if [ -z "$CAM" ] && [ -z "$TEMPUSBCAM" ] && [ -n "$BYIDCAM"]; then + echo "Camera was only detected as ${cyan}/dev/v4l/by-id${white} entry." + echo "This will be used as the camera device identifier" + fi + #only USB address + if [ -z "$CAM" ] && [ -n "$TEMPUSBCAM" ]; then + echo "${red}Camera Serial Number not detected${white}" + echo -e "Camera will be setup with physical USB address of ${cyan}$TEMPUSBCAM.${white}" + echo "The camera will have to stay plugged into this location." + USBCAM=$TEMPUSBCAM + else + echo -e "Camera detected with serial number: ${cyan}$CAM ${white}" + check_sn "$CAM" + fi + + else + echo + echo + echo "Setting up a Pi camera service for /dev/video0" + echo "Please note that mixing this setup with USB cameras may lead to issues." + echo "Don't expect extensive support for trying to fix these issues." + echo + echo + fi + + while true; do + echo "Camera Port (ENTER will increment last value found in /etc/octoprint_cameras):" + read CAMPORT + if [ -z "$CAMPORT" ]; then + CAMPORT=$(tail -1 /etc/octoprint_cameras | sed -n -e 's/^.*\(port:\)\(.*\)/\2/p') + + if [ -z "$CAMPORT" ]; then + CAMPORT=8000 + fi + CAMPORT=$((CAMPORT+1)) + fi + + if [ $CAMPORT -gt 7000 ]; then + break + else + echo "Camera Port must be greater than 7000" + fi + done + + echo "Settings can be modified after initial setup in /etc/systemd/system/cam${INUM}_$INSTANCE.service" + echo + while true; do + echo "Camera Resolution [default: 640x480]:" + read RESOLUTION + if [ -z $RESOLUTION ] + then + RESOLUTION="640x480" + break + elif [[ $RESOLUTION =~ ^[0-9]+x[0-9]+$ ]] + then + break + fi + echo "Invalid resolution" + done + + echo "Selected camera resolution: $RESOLUTION" | log + echo "Camera Framerate (use 0 for ustreamer hardware) [default: 5]:" + read FRAMERATE + if [ -z "$FRAMERATE" ]; then + FRAMERATE=5 + fi + echo "Selected camera framerate: $FRAMERATE" | log + + #Need to check if this is a one-off install + if [ -n "$camopt" ]; then + write_camera + #Pi Cam setup, replace cam_INSTANCE with /dev/video0 + if [ -n "$PI" ]; then + echo SUBSYSTEM==\"video4linux\", ATTRS{name}==\"camera0\", SYMLINK+=\"cam${INUM}_$INSTANCE\" >> /etc/udev/rules.d/99-octoprint.rules + fi + + systemctl start cam${INUM}_$INSTANCE.service + systemctl enable cam${INUM}_$INSTANCE.service + systemctl daemon-reload + udevadm control --reload-rules + udevadm trigger + main_menu + fi +} diff --git a/instance.sh b/instance.sh new file mode 100644 index 0000000..1085c58 --- /dev/null +++ b/instance.sh @@ -0,0 +1,338 @@ +#!/bin/bash + +new_instance() { + #It is possible to not create an instance after preparing,so check if this is the first + if [ -f /etc/octoprint_instances ]; then + firstrun=false + else + firstrun=true + fi + + #We can also pass this directly, from prepare.sh + firstrun=$1 + TEMPLATE='' + + get_settings + + if [ $SUDO_USER ]; then user=$SUDO_USER; fi + SCRIPTDIR=$(dirname $(readlink -f $0)) + + while true; do + echo "${green}Enter the name for new printer/instance (no spaces):${white}" + read INSTANCE + if [ -z "$INSTANCE" ]; then + echo "Please provide an instance name" + continue + fi + + if ! has-space "$INSTANCE"; then + break + else + echo "Instance names must not have spaces" + fi + done + + if [ "$firstrun" != "true" ]; then + if test -f "/etc/systemd/system/$INSTANCE.service"; then + echo "Already have an entry for $INSTANCE. Exiting." + main_menu + fi + + #Choose if should use an instance as template here + echo + echo + echo + echo "Using a template instance allows you to copy config settings" + echo "and gcode files from one instance to your new instance." + if prompt_confirm "Use an existing instance as a template?"; then + PS3="${cyan}Select template instance: ${white}" + get_instances true + select opt in "${INSTANCE_ARR[@]}" + do + if [ "$opt" == Quit ]; then + main_menu + fi + + TEMPLATE=$opt + echo "Using $opt as template." + break + done + PS3="${cyan}Select what components of the template to copy: ${white}" + options=("Config Only" "Config and Gcode") + select opt in "${options[@]}" + do + case $opt in + "Config Only") + COPY=1 + break + ;; + "Config and Gcode") + COPY=2 + break + ;;*) echo "invalid option $REPLY";; + esac + done + + else + TEMPLATE='' + fi + fi + + if prompt_confirm "Ready to begin instance creation?"; then + #CHANGE + if [ -f /etc/octoprint_instances ]; then + PORT=$(tail -1 /etc/octoprint_instances 2>/dev/null | sed -n -e 's/^.*\(port:\)\(.*\) udev:.*/\2/p') + fi + + if [ -z "$PORT" ]; then + PORT=4999 + fi + + PORT=$((PORT+1)) + echo Selected port is: $PORT + #CHANGE + OCTOUSER=$user + OCTOPATH=$OCTOEXEC + OCTOCONFIG="/home/$user" + + echo "Your new OctoPrint instance will be installed at ${cyan}/home/$user/.$INSTANCE${white}" + echo + echo + else + main_menu + fi + + if [ -n "$TEMPLATE" ]; then + BFOLD="/home/$user/.$TEMPLATE" + #check to make sure first run is complete + if grep -q 'firstRun: true' $BFOLD/config.yaml; then + echo "Template profile and admin user will have to be setup." + main_menu + fi + fi + + echo "Printer auto-detection must be completed before an instance can be created." + if prompt_confirm "Begin printer auto-detection for udev entry?"; then + detect_printer + else + echo "${magenta}Instance has not been created. Restart and do detection when you are ready.${white}" + main_menu + fi + + #Detection phase + printer_udev false + + #USB cameras + if [ "$firstrun" != "true" ]; then + if prompt_confirm "Would you like to auto detect an associated USB camera (experimental)?"; then + add_camera + fi + fi + + if prompt_confirm "Ready to write all changes. Do you want to proceed?"; then + + sudo -u $user mkdir $OCTOCONFIG/.$INSTANCE + + cat $SCRIPTDIR/octoprint_generic.service | \ + sed -e "s/OCTOUSER/$OCTOUSER/" \ + -e "s#OCTOPATH#$OCTOPATH#" \ + -e "s#OCTOCONFIG#$OCTOCONFIG#" \ + -e "s/NEWINSTANCE/$INSTANCE/" \ + -e "s/NEWPORT/$PORT/" > /etc/systemd/system/$INSTANCE.service + + #write phase + printer_udev true + + #Append instance name to list for removal tool + echo "instance:$INSTANCE port:$PORT udev:true" >> /etc/octoprint_instances + + if [ -n "$TEMPLATE" ]; then + #There may be other combinations of things to include/exclude + if [ $COPY -eq 1 ]; then + sudo -u $user rsync -r \ + --exclude 'timelapse' \ + --exclude 'uploads' \ + --exclude 'logs' \ + $BFOLD/* $OCTOCONFIG/.$INSTANCE/ + fi + if [ $COPY -eq 2 ]; then + sudo -u $user rsync -r \ + --exclude 'timelapse' \ + --exclude 'logs' \ + $BFOLD/* $OCTOCONFIG/.$INSTANCE/ + fi + echo "${magenta}Copying template files....${white}" + #cp -rp $BFOLD/* $OCTOCONFIG/.$INSTANCE + fi + + #uniquify instances + echo 'Uniquifying instance...' + BASE=$OCTOCONFIG/.$INSTANCE + sudo -u $user $OCTOEXEC --basedir $BASE config set appearance.name $INSTANCE + sudo -u $user $OCTOEXEC --basedir $BASE config set server.commands.serverRestartCommand "sudo systemctl restart $INSTANCE" + sudo -u $user $OCTOEXEC --basedir $BASE config set server.commands.systemRestartCommand "sudo reboot" + sudo -u $user $OCTOEXEC --basedir $BASE config set plugins.discovery.upnpUuid $(uuidgen) + sudo -u $user $OCTOEXEC --basedir $BASE config set plugins.errortracking.unique_id $(uuidgen) + sudo -u $user $OCTOEXEC --basedir $BASE config set plugins.tracking.unique_id $(uuidgen) + sudo -u $user $OCTOEXEC --basedir $BASE config set serial.port /dev/octo_$INSTANCE + #clear additional ports + sudo -u $user $OCTOEXEC --basedir $BASE config remove serial.additionalPorts + sudo -u $user $OCTOEXEC --basedir $BASE config append_value serial.additionalPorts "/dev/octo_$INSTANCE" + sudo -u $user $OCTOEXEC --basedir $BASE config set feature.modelSizeDetection false --bool + sudo -u $user $OCTOEXEC --basedir $BASE config set webcam.ffmpeg /usr/bin/ffmpeg + + if [ "$HAPROXY" == true ]; then + HAversion=$(haproxy -v | sed -n 's/^.*version \([0-9]\).*/\1/p') + SEDREPLACE="#$INSTANCE start\n\ + acl is_$INSTANCE url_beg /$INSTANCE\n\ + http-request redirect scheme http drop-query append-slash if is_$INSTANCE ! { path_beg /$INSTANCE/ }\n\ + use_backend $INSTANCE if { path_beg /$INSTANCE/ }\n\ + #$INSTANCE stop" + + sed -i "/option forwardfor except 127.0.0.1/a $SEDREPLACE" /etc/haproxy/haproxy.cfg + echo "#$INSTANCE start" >> /etc/haproxy/haproxy.cfg + echo "backend $INSTANCE" >> /etc/haproxy/haproxy.cfg + if [ $HAversion -gt 1 ]; then + echo " http-request replace-path /$INSTANCE/(.*) /\1" >> /etc/haproxy/haproxy.cfg + echo " acl needs_scheme req.hdr_cnt(X-Scheme) eq 0" >> /etc/haproxy/haproxy.cfg + echo " http-request add-header X-Scheme https if needs_scheme { ssl_fc }" >> /etc/haproxy/haproxy.cfg + echo " http-request add-header X-Scheme http if needs_scheme !{ ssl_fc }" >> /etc/haproxy/haproxy.cfg + echo " http-request add-header X-Script-Name /$INSTANCE" >> /etc/haproxy/haproxy.cfg + echo " server octoprint1 127.0.0.1:$PORT" >> /etc/haproxy/haproxy.cfg + echo " option forwardfor" >> /etc/haproxy/haproxy.cfg + else + echo " reqrep ^([^\ :]*)\ /$INSTANCE/(.*) \1\ /\2" >> /etc/haproxy/haproxy.cfg + echo " server octoprint1 127.0.0.1:$PORT" >> /etc/haproxy/haproxy.cfg + echo " option forwardfor" >> /etc/haproxy/haproxy.cfg + echo " acl needs_scheme req.hdr_cnt(X-Scheme) eq 0" >> /etc/haproxy/haproxy.cfg + echo " reqadd X-Scheme:\ https if needs_scheme { ssl_fc }" >> /etc/haproxy/haproxy.cfg + echo " reqadd X-Scheme:\ http if needs_scheme !{ ssl_fc }" >> /etc/haproxy/haproxy.cfg + echo " reqadd X-Script-Name:\ /$INSTANCE" >> /etc/haproxy/haproxy.cfg + fi + + echo "#$INSTANCE stop" >> /etc/haproxy/haproxy.cfg + + #restart haproxy + sudo systemctl restart haproxy.service + + fi + + if [[ -n $CAM || -n $USBCAM ]]; then + write_camera + fi + + #Reset udev + udevadm control --reload-rules + udevadm trigger + systemctl daemon-reload + sleep 1 + + #Start and enable system processes + systemctl start $INSTANCE.service + systemctl enable $INSTANCE.service + if [[ -n $CAM || -n $USBCAM || -n $BYIDCAM ]]; then + systemctl start cam_$INSTANCE.service + systemctl enable cam_$INSTANCE.service + fi + else + main_menu + fi + + if [ "$firstrun" == "true" ]; then + firstrun_install + else + main_menu + fi + +} + +detect_printer() { + echo + echo + dmesg -C + echo "Plug your printer in via USB now (detection time-out in 1 min)" + counter=0 + while [[ -z "$UDEV" ]] && [[ $counter -lt 60 ]]; do + TEMPUSB=$(dmesg | sed -n -e 's/^.*\(cdc_acm\|ftdi_sio\|ch341\|cp210x\|ch34x\) \([0-9].*[0-9]\): \(tty.*\|FTD.*\|ch341-uart.*\|cp210x\|ch34x\).*/\2/p') + UDEV=$(dmesg | sed -n -e 's/^.*SerialNumber: //p') + counter=$(( $counter + 1 )) + if [[ -n "$TEMPUSB" ]] && [[ -z "$UDEV" ]]; then + break + fi + sleep 1 + done + dmesg -C +} + +printer_udev() { + write=$1 + if [ "$write" == true ]; then + #Printer udev identifier technique - either Serial number or USB port + #Serial Number + if [ -n "$UDEV" ]; then + echo SUBSYSTEM==\"tty\", ATTRS{serial}==\"$UDEV\", SYMLINK+=\"octo_$INSTANCE\" >> /etc/udev/rules.d/99-octoprint.rules + fi + + #USB port + if [ -n "$USB" ]; then + echo KERNELS==\"$USB\",SUBSYSTEM==\"tty\",SYMLINK+=\"octo_$INSTANCE\" >> /etc/udev/rules.d/99-octoprint.rules + fi + else + #No serial number + if [ -z "$UDEV" ]; then + echo "Printer Serial Number not detected" + if prompt_confirm "Do you want to use the physical USB port to assign the udev entry? If you use this any USB hubs and printers detected this way must stay plugged into the same USB positions on your machine as they are right now"; then + echo + USB=$TEMPUSB + echo "Your printer will be setup at the following usb address: ${cyan}$USB${white}" + echo + else + main_menu + fi + else + echo -e "Serial number detected as: ${cyan}$UDEV${white}" + check_sn "$UDEV" + echo + fi + #Failed state. Nothing detected + if [ -z "$UDEV" ] && [ -z "$TEMPUSB" ]; then + echo + echo "${red}No printer was detected during the detection period.${white}" + echo "Check your USB cable (power only?) and try again." + echo + echo + main_menu + fi + fi +} + +remove_instance() { + opt=$1 + #disable and remove service file + if [ -f /etc/systemd/system/$opt.service ]; then + systemctl stop $opt.service + systemctl disable $opt.service + rm /etc/systemd/system/$opt.service + fi + + #Get all cameras associated with this instance. + #Is this right? + get_cameras + for camera in "${CAMERA_ARR[@]}"; do + remove_camera $camera + done + + #remove udev entry + sed -i "/$opt/d" /etc/udev/rules.d/99-octoprint.rules + #remove files + rm -rf /home/$user/.$opt + #remove from octoprint_instances + sed -i "/$opt/d" /etc/octoprint_instances + #remove haproxy entry + if [ "$HAPROXY" == true ]; then + sed -i "/use_backend $opt/d" /etc/haproxy/haproxy.cfg + sed -i "/#$opt start/,/#$opt stop/d" /etc/haproxy/haproxy.cfg + systemctl restart haproxy.service + fi + +} \ No newline at end of file diff --git a/menu.sh b/menu.sh new file mode 100644 index 0000000..6d83e3b --- /dev/null +++ b/menu.sh @@ -0,0 +1,258 @@ +#!/bin/bash +source $SCRIPTDIR/prepare.sh +white=$(echo -en "\e[39m") +green=$(echo -en "\e[92m") +magenta=$(echo -en "\e[35m") +cyan=$(echo -en "\e[96m") +yellow=$(echo -en "\e[93m") + +main_menu() { + VERSION=1.0.0 + #reset + UDEV='' + TEMPUSB='' + CAM='' + TEMPUSBCAM='' + INSTANCE='' + INSTALL='' + CAMHAPROXY='' + echo + echo + echo "${cyan}*************************${white}" + echo "${green}octoprint_deploy${white} $VERSION" + echo "${cyan}*************************${white}" + echo + PS3="${green}Select operation: ${white}" + if [ -f "/etc/octoprint_deploy" ]; then + options=("Add instance" "Delete instance" "Add Camera" "Delete Camera" "Utilities" "Backup Menu" "Update" "Quit") + else + options=("Prepare system" "Update" "Quit") + fi + + select opt in "${options[@]}" + do + case $opt in + "Prepare system") + detect_installs + break + ;; + "Add instance") + new_instance + break + ;; + "Delete instance") + remove_instance_menu + break + ;; + "Add Camera") + add_camera + break + ;; + "Delete Camera") + remove_camera_menu + break + ;; + "Utilities") + utility_menu + break + ;; + "Backup Menu") + backup_menu + break + ;; + "Update") + octo_deploy_update + break + ;; + "Quit") + exit 1 + ;; + *) echo "invalid option $REPLY";; + esac + done +} + +remove_instance_menu() { + echo + echo + get_settings + if [ $SUDO_USER ]; then user=$SUDO_USER; fi + if [ -f "/etc/octoprint_instances" ]; then + + PS3="${green}Select instance number to remove: ${white}" + get_instances true + select opt in "${INSTANCE_ARR[@]}" + do + if [ "$opt" == Quit ]; then + main_menu + fi + echo "Selected instance to remove: $opt" + break + done + + if prompt_confirm "Do you want to remove everything associated with this instance?"; then + remove_instance $opt + fi + fi + main_menu +} + +remove_camera_menu() { + get_settings + PS3="${green}Select camera number to remove: ${white}" + readarray -t cameras < <(cat /etc/octoprint_cameras | sed -n -e 's/^camera:\([[:graph:]]*\) .*/\1/p') + cameras+=("Quit") + + select camera in "${cameras[@]}" + do + if [ "$camera" == Quit ]; then + main_menu + fi + + echo "Removing udev, service files, and haproxy entry for $camera" + remove_camera $camera + main_menu + done +} + +utility_menu() { + echo + echo + PS3="${green}Select an option: ${white}" + options=("Instance Status" "USB Port Testing" "Sync Users" "Share Uploads" "Change Streamer" "Set Global Config" "Udev Menu" "Diagnostic Output" "Quit") + select opt in "${options[@]}" + do + case $opt in + "Instance Status") + instance_status + break + ;; + "USB Port Testing") + usb_testing + break + ;; + "Sync Users") + sync_users + break + ;; + "Share Uploads") + share_uploads + break + ;; + "Set Global Config") + global_config + break + ;; + "Udev Menu") + udev_menu + break + ;; + "Change Streamer") + change_streamer + break + ;; + "Diagnostic Output") + diagnostics + break + ;; + "Quit") + main_menu + break + ;;*) echo "invalid option $REPLY";; + esac + done +} + +udev_menu() { + echo + echo + PS3="${green}Select an option: ${white}" + options=("Add printer udev rule" "Remove printer udev rule" "Add camera udev rule" "Remove camera udev rule" "Quit") + select opt in "${options[@]}" + do + case $opt in + "Add printer udev rule") + add_udev + break + ;; + "Remove printer udev rule") + remove_udev + break + ;; + "Add camera udev rule") + add_udev_camera + break + ;; + "Remove camera udev rule") + remove_udev_camera + break + ;; + "Quit") + main_menu + break + ;;*) echo "invalid option $REPLY";; + esac + done + +} + +backup_menu() { + echo + echo + PS3="${green}Select an option: ${white}" + options=("Create Backup" "Restore Backup" "Quit") + select opt in "${options[@]}" + do + case $opt in + "Create Backup") + create_menu + break + ;; + "Restore Backup") + restore_menu + break + break + ;; + "Quit") + main_menu + break + ;;*) echo "invalid option $REPLY";; + esac + done +} + +create_menu() { + echo + echo + PS3="${green}Select instance number to backup: ${white}" + readarray -t options < <(cat /etc/octoprint_instances | sed -n -e 's/^instance:\([[:graph:]]*\) .*/\1/p') + options+=("Quit") + select opt in "${options[@]}" + do + if [ "$opt" == Quit ]; then + main_menu + fi + + echo "Selected instance to backup: $opt" + back_up $opt + main_menu + done +} + +restore_menu() { + echo + echo + PS3="${green}Select backup to restore: ${white}" + readarray -t options < <(ls /home/$user/*.tar.gz) + options+=("Quit") + select opt in "${options[@]}" + do + if [ "$opt" == Quit ] || [ "$opt" == generic ]; then + main_menu + fi + + echo "Selected $opt to restore" + tar --same-owner -pxvf $opt + main_menu + done +} diff --git a/octoprint_deploy.sh b/octoprint_deploy.sh index 4d89abb..8d47d2b 100755 --- a/octoprint_deploy.sh +++ b/octoprint_deploy.sh @@ -19,1600 +19,18 @@ if [ "$user" == root ]; then exit fi -#Get abbreviated architecture -ARCH=$(arch) -ARCH=${ARCH:0:3} - -get_settings() { - #Get octoprint_deploy settings, all of which are written on system prepare - if [ -f /etc/octoprint_deploy ]; then - TYPE=$(cat /etc/octoprint_deploy | sed -n -e 's/^type: \(\.*\)/\1/p') - if [ "$TYPE" == linux ]; then - OCTOEXEC="sudo -u $user /home/$user/OctoPrint/bin/octoprint" - else - OCTOEXEC="sudo -u $user /home/$user/oprint/bin/octoprint" - fi - STREAMER=$(cat /etc/octoprint_deploy | sed -n -e 's/^streamer: \(\.*\)/\1/p') - #echo $STREAMER - HAPROXY=$(cat /etc/octoprint_deploy | sed -n -e 's/^haproxy: \(\.*\)/\1/p') - #echo $HAPROXY - HAPROXYNEW=$(cat /etc/octoprint_deploy | sed -n -e 's/^haproxynew: \(\.*\)/\1/p') - if [ -z "$HAPROXYNEW" ]; then - HAPROXYNEW="false" - fi - fi -} - -# from stackoverflow.com/questions/3231804 -prompt_confirm() { - while true; do - read -r -n 1 -p "${1:-Continue?} [y/n]: " REPLY - case $REPLY in - [yY]) echo ; return 0 ;; - [nN]) echo ; return 1 ;; - *) printf " \033[31m %s \n\033[0m" "invalid input" - esac - done -} -# from unix.stackexchange.com/questions/391293 -log () { - if [ -z "$1" ]; then - cat - else - printf '%s\n' "$@" - fi | tee -a "$logfile" -} - -#https://gist.github.com/wellsie/56a468a1d53527fec827 -has-space () { - [[ "$1" != "${1%[[:space:]]*}" ]] && return 0 || return 1 -} - -new_instance () { - - echo "$(date) starting instance installation" | log - get_settings - - if [ $SUDO_USER ]; then user=$SUDO_USER; fi - SCRIPTDIR=$(dirname $(readlink -f $0)) - PIDEFAULT="/home/$user/oprint/bin/octoprint" - BUDEFAULT="/home/$user/OctoPrint/bin/octoprint" - if [ -z "$TYPE" ]; then - echo "No installation type found. Have you run system prepare?" - main_menu - fi - - if [ "$TYPE" == octopi ]; then - INSTALL=1 - DAEMONPATH=$PIDEFAULT - fi - - if [ "$TYPE" == linux ]; then - INSTALL=2 - DAEMONPATH=$BUDEFAULT - fi - - while true; do - echo "Enter the name for new printer/instance (no spaces):" - read INSTANCE - if [ -z "$INSTANCE" ]; then - echo "No instance given. Exiting" | log - main_menu - fi - - if ! has-space "$INSTANCE"; then - break - else - echo "Instance names must not have spaces" - fi - done - - if test -f "/etc/systemd/system/$INSTANCE.service"; then - echo "Already have an entry for $INSTANCE. Exiting." | log - main_menu - fi - - if prompt_confirm "Use all default values?"; then - PORT=$(tail -1 /etc/octoprint_instances | sed -n -e 's/^.*\(port:\)\(.*\)/\2/p') - if [ -z "$PORT" ]; then - PORT=5000 - fi - PORT=$((PORT+1)) - echo Selected port is: $PORT | log - OCTOUSER=$user - OCTOPATH=$DAEMONPATH - OCTOCONFIG="/home/$user" - BFOLD="/home/$user/.octoprint" - echo "Your OctoPrint instance will be installed at /home/$user/.$INSTANCE" - echo - echo - else - echo "Port on which this instance will run (ENTER will increment from last value in /etc/octoprint_instances):" - read PORT - if [ -z "$PORT" ]; then - PORT=$(tail -1 /etc/octoprint_instances | sed -n -e 's/^.*\(port:\)\(.*\)/\2/p') - - if [ -z "$PORT" ]; then - PORT=5000 - fi - - PORT=$((PORT+1)) - echo Selected port is: $PORT | log - - fi - - if [ -f /etc/octoprint_instances ]; then - if grep -q $PORT /etc/octoprint_instances; then - echo "Port may be in use! Check /etc/octoprint_instances and select a different port. Exiting." | log - main_menu - fi - fi - - #collect user, basedir path, daemon path - echo "Octoprint Daemon User [$user]:" - read OCTOUSER - if [ -z "$OCTOUSER" ]; then - OCTOUSER=$user - fi - - echo "Octoprint Executable Daemon Path [$DAEMONPATH]:" - read OCTOPATH - if [ -z "$OCTOPATH" ]; then - OCTOPATH=$DAEMONPATH - fi - - if [ -f "$OCTOPATH" ]; then - echo "Executable path is valid" | log - else - echo "Exectuable path is not valid! Aborting" | log - main_menu - fi - - echo "Octoprint Config Path (where the hidden instance directory will be) [/home/$user/]:" - read OCTOCONFIG - if [ -z "$OCTOCONFIG" ]; then - OCTOCONFIG="/home/$user" - fi - - #octoprint_base is the generic .octoprint folder that contains all configuration, upload, etc. - echo "Octoprint instance template path [/home/$user/.octoprint]:" - read BFOLD - if [ -z "$BFOLD" ]; then - BFOLD="/home/$user/.octoprint" - fi - - if [ -d "$BFOLD" ]; then - echo "Template path is valid" | log - else - echo "Template path is not valid! Aborting" | log - main_menu - fi - fi - - #check to make sure first run is complete - if grep -q 'firstRun: true' $BFOLD/config.yaml; then - echo "WARNING!! You must setup the template profile and admin user before continuing" | log - main_menu - fi - - echo "You must complete the auto-detection to create an instance." - if prompt_confirm "Begin auto-detect printer serial number for udev entry?" - then - detect_printer - else - echo "OK. Restart when you are ready" | log; exit 0 - fi - - #Failed state. Nothing detected - if [ -z "$UDEV" ] && [ -z "$TEMPUSB" ]; then - echo - echo -e "\033[0;31mNo printer was detected during the detection period.\033[0m Check your USB cable (power only?) and try again." - echo - echo - main_menu - fi - - #No serial number - if [ -z "$UDEV" ]; then - echo "Printer Serial Number not detected" - if prompt_confirm "Do you want to use the physical USB port to assign the udev entry? If you use this any USB hubs and printers detected this way must stay plugged into the same USB positions on your machine as they are right now"; then - echo - USB=$TEMPUSB - echo -e "Your printer will be setup at the following usb address: $USB" | log - echo - else - main_menu - fi - else - echo -e "Serial number detected as: $UDEV" | log - check_sn "$UDEV" - echo - fi - - echo - - #USB cameras - if [[ -n $INSTALL ]]; then - if prompt_confirm "Would you like to auto detect an associated USB camera (experimental)?" - then - add_camera - fi - fi - echo - - if prompt_confirm "Ready to write all changes. Do you want to proceed?" - then - cat $SCRIPTDIR/octoprint_generic.service | \ - sed -e "s/OCTOUSER/$OCTOUSER/" \ - -e "s#OCTOPATH#$OCTOPATH#" \ - -e "s#OCTOCONFIG#$OCTOCONFIG#" \ - -e "s/NEWINSTANCE/$INSTANCE/" \ - -e "s/NEWPORT/$PORT/" > /etc/systemd/system/$INSTANCE.service - - #If a default octoprint service exists, stop and disable it - if [ -f "/etc/systemd/system/octoprint_default.service" ]; then - systemctl stop octoprint_default.service - systemctl disable octoprint_default.service - fi - - #stop and disable default octoprint service (octopi) - if [ -f "/etc/systemd/system/octoprint.service" ]; then - systemctl stop octoprint.service - systemctl disable octoprint.service - fi - - #Printer udev identifier technique - either Serial number or USB port - #Serial Number - if [ -n "$UDEV" ]; then - echo SUBSYSTEM==\"tty\", ATTRS{serial}==\"$UDEV\", SYMLINK+=\"octo_$INSTANCE\" >> /etc/udev/rules.d/99-octoprint.rules - fi - - #USB port - if [ -n "$USB" ]; then - echo KERNELS==\"$USB\",SUBSYSTEM==\"tty\",SYMLINK+=\"octo_$INSTANCE\" >> /etc/udev/rules.d/99-octoprint.rules - fi - - #Append instance name to list for removal tool - echo instance:$INSTANCE port:$PORT >> /etc/octoprint_instances - - #copy all files to our new directory - cp -rp $BFOLD $OCTOCONFIG/.$INSTANCE - - #uniquify instances - echo 'Uniquifying instance...' - #Do config.yaml modifications here - cat $BFOLD/config.yaml | sed -e "s/INSTANCE/$INSTANCE/" > $OCTOCONFIG/.$INSTANCE/config.yaml - $DAEMONPATH --basedir $OCTOCONFIG/.$INSTANCE config set plugins.discovery.upnpUuid $(uuidgen) - $DAEMONPATH --basedir $OCTOCONFIG/.$INSTANCE config set plugins.errortracking.unique_id $(uuidgen) - $DAEMONPATH --basedir $OCTOCONFIG/.$INSTANCE config set plugins.tracking.unique_id $(uuidgen) - $DAEMONPATH --basedir $OCTOCONFIG/.$INSTANCE config set serial.port /dev/octo_$INSTANCE - $DAEMONPATH --basedir $OCTOCONFIG/.$INSTANCE config set webcam.ffmpeg /usr/bin/ffmpeg - - if [ "$HAPROXY" == true ]; then - HAversion=$(haproxy -v | sed -n 's/^.*version \([0-9]\).*/\1/p') - #find frontend line, do insert - #Don't know how to do the formatting correctly here. This works, however. - - SEDREPLACE="#$INSTANCE start\n\ - acl is_$INSTANCE url_beg /$INSTANCE\n\ - http-request redirect scheme http drop-query append-slash if is_$INSTANCE ! { path_beg /$INSTANCE/ }\n\ - use_backend $INSTANCE if { path_beg /$INSTANCE/ }\n\ - #$INSTANCE stop" - - sed -i "/option forwardfor except 127.0.0.1/a $SEDREPLACE" /etc/haproxy/haproxy.cfg - echo "#$INSTANCE start" >> /etc/haproxy/haproxy.cfg - echo "backend $INSTANCE" >> /etc/haproxy/haproxy.cfg - if [ $HAversion -gt 1 ]; then - echo " http-request replace-path /$INSTANCE/(.*) /\1" >> /etc/haproxy/haproxy.cfg - echo " acl needs_scheme req.hdr_cnt(X-Scheme) eq 0" >> /etc/haproxy/haproxy.cfg - echo " http-request add-header X-Scheme https if needs_scheme { ssl_fc }" >> /etc/haproxy/haproxy.cfg - echo " http-request add-header X-Scheme http if needs_scheme !{ ssl_fc }" >> /etc/haproxy/haproxy.cfg - echo " http-request add-header X-Script-Name /$INSTANCE" >> /etc/haproxy/haproxy.cfg - echo " server octoprint1 127.0.0.1:$PORT" >> /etc/haproxy/haproxy.cfg - echo " option forwardfor" >> /etc/haproxy/haproxy.cfg - else - echo " reqrep ^([^\ :]*)\ /$INSTANCE/(.*) \1\ /\2" >> /etc/haproxy/haproxy.cfg - echo " server octoprint1 127.0.0.1:$PORT" >> /etc/haproxy/haproxy.cfg - echo " option forwardfor" >> /etc/haproxy/haproxy.cfg - echo " acl needs_scheme req.hdr_cnt(X-Scheme) eq 0" >> /etc/haproxy/haproxy.cfg - echo " reqadd X-Scheme:\ https if needs_scheme { ssl_fc }" >> /etc/haproxy/haproxy.cfg - echo " reqadd X-Scheme:\ http if needs_scheme !{ ssl_fc }" >> /etc/haproxy/haproxy.cfg - echo " reqadd X-Script-Name:\ /$INSTANCE" >> /etc/haproxy/haproxy.cfg - fi - - echo "#$INSTANCE stop" >> /etc/haproxy/haproxy.cfg - - #restart haproxy - sudo systemctl restart haproxy.service - - fi - - if [[ -n $CAM || -n $USBCAM ]]; then - write_camera - fi - - #Reset udev - udevadm control --reload-rules - udevadm trigger - systemctl daemon-reload - sleep 1 - - #Start and enable system processes - systemctl start $INSTANCE.service - systemctl enable $INSTANCE.service - if [[ -n $CAM || -n $USBCAM ]]; then - systemctl start cam_$INSTANCE.service - systemctl enable cam_$INSTANCE.service - fi - - fi - main_menu - -} - -write_camera() { - - get_settings - if [ -z "$STREAMER" ]; then - STREAMER="mjpg-streamer" - fi - - #mjpg-streamer - if [ "$STREAMER" == mjpg-streamer ]; then - cat $SCRIPTDIR/octocam_mjpg.service | \ - sed -e "s/OCTOUSER/$OCTOUSER/" \ - -e "s/OCTOCAM/cam${INUM}_$INSTANCE/" \ - -e "s/RESOLUTION/$RESOLUTION/" \ - -e "s/FRAMERATE/$FRAMERATE/" \ - -e "s/CAMPORT/$CAMPORT/" > $SCRIPTDIR/cam${INUM}_$INSTANCE.service - fi - - #ustreamer - if [ "$STREAMER" == ustreamer ]; then - cat $SCRIPTDIR/octocam_ustream.service | \ - sed -e "s/OCTOUSER/$OCTOUSER/" \ - -e "s/OCTOCAM/cam${INUM}_$INSTANCE/" \ - -e "s/RESOLUTION/$RESOLUTION/" \ - -e "s/FRAMERATE/$FRAMERATE/" \ - -e "s/CAMPORT/$CAMPORT/" > $SCRIPTDIR/cam${INUM}_$INSTANCE.service - fi - - mv $SCRIPTDIR/cam${INUM}_$INSTANCE.service /etc/systemd/system/ - echo $CAMPORT >> /etc/camera_ports - #config.yaml modifications - only if INUM not set - if [ -z "$INUM" ]; then - sudo -u $user $OCTOEXEC --basedir $OCTOCONFIG/.$INSTANCE config set plugins.classicwebcam.snapshot "http://localhost:$CAMPORT?action=snapshot" - - if [ -z "$CAMHAPROXY" ]; then - sudo -u $user $OCTOEXEC --basedir $OCTOCONFIG/.$INSTANCE config set plugins.classicwebcam.stream "http://$(hostname).local:$CAMPORT?action=stream" - else - sudo -u $user $OCTOEXEC --basedir $OCTOCONFIG/.$INSTANCE config set plugins.classicwebcam.stream "/cam_$INSTANCE/?action=stream" - fi - sudo -u $user $OCTOEXEC --basedir $OCTOCONFIG/.$INSTANCE config append_value --json system.actions "{\"action\": \"Reset video streamer\", \"command\": \"sudo systemctl restart cam_$INSTANCE\", \"name\": \"Restart webcam\"}" - - if prompt_confirm "Instance must be restarted for settings to take effect. Restart now?"; then - systemctl restart $INSTANCE - fi - fi - - #Either Serial number or USB port - #Serial Number - if [ -n "$CAM" ]; then - echo SUBSYSTEM==\"video4linux\", ATTRS{serial}==\"$CAM\", ATTR{index}==\"0\", SYMLINK+=\"cam${INUM}_$INSTANCE\" >> /etc/udev/rules.d/99-octoprint.rules - fi - - #USB port camera - if [ -n "$USBCAM" ]; then - echo SUBSYSTEM==\"video4linux\",KERNELS==\"$USBCAM\", SUBSYSTEMS==\"usb\", ATTR{index}==\"0\", DRIVERS==\"uvcvideo\", SYMLINK+=\"cam${INUM}_$INSTANCE\" >> /etc/udev/rules.d/99-octoprint.rules - fi - - if [ -n "$CAMHAPROXY" ]; then - HAversion=$(haproxy -v | sed -n 's/^.*version \([0-9]\).*/\1/p') - #find frontend line, do insert - sed -i "/use_backend $INSTANCE if/a\ use_backend cam${INUM}_$INSTANCE if { path_beg /cam${INUM}_$INSTANCE/ }" /etc/haproxy/haproxy.cfg - if [ $HAversion -gt 1 ]; then - EXTRACAM="backend cam${INUM}_$INSTANCE\n\ - http-request replace-path /cam${INUM}_$INSTANCE/(.*) /|\1\n\ - server webcam1 127.0.0.1:$CAMPORT" - else - EXTRACAM="backend cam${INUM}_$INSTANCE\n\ - reqrep ^([^|\ :]*)|\ /cam${INUM}_$INSTANCE/(.*) |\1|\ /|\2 \n\ - server webcam1 127.0.0.1:$CAMPORT" - fi - - echo "#cam${INUM}_$INSTANCE start" >> /etc/haproxy/haproxy.cfg - sed -i "/#cam${INUM}_$INSTANCE start/a $EXTRACAM" /etc/haproxy/haproxy.cfg - #these are necessary because sed append seems to have issues with escaping for the /\1 - sed -i 's/\/|1/\/\\1/' /etc/haproxy/haproxy.cfg - sed -i 's/\/|2/\/\\2/' /etc/haproxy/haproxy.cfg - #haproxy 1.x correction - sed -i 's/|/\\/g' /etc/haproxy/haproxy.cfg - echo "#cam${INUM}_$INSTANCE stop" >> /etc/haproxy/haproxy.cfg - - systemctl restart haproxy - fi -} - -add_camera() { - PI=$1 - INUM='' - get_settings - if [ $SUDO_USER ]; then user=$SUDO_USER; fi - echo 'Adding camera' | log - if [ -z "$INSTANCE" ]; then - PS3='Select instance number to add camera to: ' - readarray -t options < <(cat /etc/octoprint_instances | sed -n -e 's/^instance:\([[:graph:]]*\) .*/\1/p') - options+=("Quit") - unset 'options[0]' - select camopt in "${options[@]}" - do - if [ "$camopt" == Quit ]; then - main_menu - fi - echo "Selected instance for camera: $camopt" | log - INSTANCE=$camopt - OCTOCONFIG="/home/$user/" - OCTOUSER=$user - if grep -q "cam_$INSTANCE" /etc/udev/rules.d/99-octoprint.rules; then - echo "It appears this instance already has at least one camera." - if prompt_confirm "Do you want to add an additional camera for this instance?"; then - echo "Enter a number for this camera." - echo "Ex. entering 2 will setup a service called cam2_$INSTANCE" - echo - read INUM - if [ -z "$INUM" ]; then - echo "No value given, setting as 2" - INUM='2' - fi - else - main_menu - fi - fi - break - done - fi - #for now just set a flag that we are going to write cameras behind haproxy - if [ "$HAPROXY" == true ]; then - if prompt_confirm "Add cameras to haproxy?"; then - CAMHAPROXY=1 - fi - fi - - if [ -z "$PI" ]; then - detect_camera - if [ -n "$NOSERIAL" ] && [ -n "$CAM" ]; then - unset CAM - fi - #Failed state. Nothing detected - if [ -z "$CAM" ] && [ -z "$TEMPUSBCAM" ] ; then - echo - echo -e "\033[0;31mNo camera was detected during the detection period.\033[0m" - echo - return - fi - - if [ -z "$CAM" ]; then - echo "Camera Serial Number not detected" | log - echo -e "Camera will be setup with physical USB address of \033[0;32m $TEMPUSBCAM.\033[0m" | log - echo "The camera will have to stay plugged into this location." | log - USBCAM=$TEMPUSBCAM - else - echo -e "Camera detected with serial number: \033[0;32m $CAM \033[0m" | log - check_sn "$CAM" - fi - - else - echo - echo - echo "Setting up a Pi camera service for /dev/video0" - echo "Please note that mixing this setup with USB cameras may lead to issues." - echo "Don't expect extensive support for trying to fix these issues." - echo - echo - fi - - while true; do - echo "Camera Port (ENTER will increment last value in /etc/camera_ports):" - read CAMPORT - if [ -z "$CAMPORT" ]; then - CAMPORT=$(tail -1 /etc/camera_ports) - - if [ -z "$CAMPORT" ]; then - CAMPORT=8000 - fi - CAMPORT=$((CAMPORT+1)) - fi - - if [ $CAMPORT -gt 7000 ]; then - break - else - echo "Camera Port must be greater than 7000" - fi - - - done - - echo "Settings can be modified after initial setup in /etc/systemd/system/cam${INUM}_$INSTANCE.service" - echo - while true; do - echo "Camera Resolution [default: 640x480]:" - read RESOLUTION - if [ -z $RESOLUTION ] - then - RESOLUTION="640x480" - break - elif [[ $RESOLUTION =~ ^[0-9]+x[0-9]+$ ]] - then - break - fi - echo "Invalid resolution" - done - echo "Selected camera resolution: $RESOLUTION" | log - echo "Camera Framerate (use 0 for ustreamer hardware) [default: 5]:" - read FRAMERATE - if [ -z "$FRAMERATE" ]; then - FRAMERATE=5 - fi - echo "Selected camera framerate: $FRAMERATE" | log - - #Need to check if this is a one-off install - if [ -n "$camopt" ]; then - write_camera - #Pi Cam setup, replace cam_INSTANCE with /dev/video0 - if [ -n "$PI" ]; then - echo SUBSYSTEM==\"video4linux\", ATTRS{name}==\"camera0\", SYMLINK+=\"cam${INUM}_$INSTANCE\" >> /etc/udev/rules.d/99-octoprint.rules - fi - systemctl start cam${INUM}_$INSTANCE.service - systemctl enable cam${INUM}_$INSTANCE.service - systemctl daemon-reload - udevadm control --reload-rules - udevadm trigger - main_menu - fi -} - -detect_printer() { - echo - echo - dmesg -C - echo "Plug your printer in via USB now (detection time-out in 1 min)" - counter=0 - while [[ -z "$UDEV" ]] && [[ $counter -lt 60 ]]; do - TEMPUSB=$(dmesg | sed -n -e 's/^.*\(cdc_acm\|ftdi_sio\|ch341\|cp210x\|ch34x\) \([0-9].*[0-9]\): \(tty.*\|FTD.*\|ch341-uart.*\|cp210x\|ch34x\).*/\2/p') - UDEV=$(dmesg | sed -n -e 's/^.*SerialNumber: //p') - counter=$(( $counter + 1 )) - if [[ -n "$TEMPUSB" ]] && [[ -z "$UDEV" ]]; then - break - fi - sleep 1 - done - dmesg -C -} - -detect_camera() { - dmesg -C - echo "Plug your camera in via USB now (detection time-out in 1 min)" - counter=0 - while [[ -z "$CAM" ]] && [[ $counter -lt 60 ]]; do - CAM=$(dmesg | sed -n -e 's/^.*SerialNumber: //p') - TEMPUSBCAM=$(dmesg | sed -n -e 's|^.*input:.*/\(.*\)/input/input.*|\1|p') - counter=$(( $counter + 1 )) - if [[ -n "$TEMPUSBCAM" ]] && [[ -z "$CAM" ]]; then - break - fi - sleep 1 - done - dmesg -C -} - - - -usb_testing() { - echo - echo - echo "Testing printer USB" | log - detect_printer - echo "Detected device at $TEMPUSB" | log - echo "Serial Number detected: $UDEV" | log - main_menu -} - -#https://askubuntu.com/questions/39497 -deb_packages() { - #All extra packages needed can be added here for deb based systems. Only available will be selected. - apt-cache --generate pkgnames \ - | grep --line-regexp --fixed-strings \ - -e make \ - -e v4l-utils \ - -e python-is-python3 \ - -e python3-venv \ - -e python3.9-venv \ - -e python3.10-venv \ - -e virtualenv \ - -e python3-dev \ - -e python3-setuptools \ - -e build-essential \ - -e python3-setuptools \ - -e libyaml-dev \ - -e python3-pip \ - -e cmake \ - -e libjpeg8-dev \ - -e libjpeg62-turbo-dev \ - -e gcc \ - -e g++ \ - -e libevent-dev \ - -e libjpeg-dev \ - -e libbsd-dev \ - -e ffmpeg \ - -e uuid-runtime\ - -e ssh\ - -e libffi-dev\ - -e haproxy\ - | xargs apt-get install -y | log - - #pacakges to REMOVE go here - apt-cache --generate pkgnames \ - | grep --line-regexp --fixed-strings \ - -e brltty \ - | xargs apt-get remove -y | log - -} - -prepare () { - echo - echo - MOVE=0 - echo 'Beginning system preparation' | log - PS3='Installation type: ' - options=("OctoPi" "Ubuntu 20+, Mint, Debian, Raspberry Pi OS" "Fedora/CentOS" "ArchLinux" "Quit") - select opt in "${options[@]}" - do - case $opt in - "OctoPi") - INSTALL=1 - break - ;; - "Ubuntu 20+, Mint, Debian, Raspberry Pi OS") - INSTALL=2 - break - ;; - "Fedora/CentOS") - INSTALL=3 - break - ;; - "ArchLinux") - INSTALL=4 - break - ;; - "Quit") - exit 1 - ;; - *) echo "invalid option $REPLY";; - esac - done - - if [ $INSTALL -eq 1 ] && [[ "$ARCH" != arm ]]; then - echo - echo - echo "WARNING! You have selected OctoPi, but are not using an ARM processor." - echo "If you are using another linux distribution, select it from the list." - echo "Unless you really know what you are doing, select N now." - echo - echo - if prompt_confirm "Continue with OctoPi?"; then - echo "OK!" - else - main_menu - fi - fi - echo - echo - if prompt_confirm "Ready to begin?" - then - echo 'Adding current user to dialout and video groups.' - usermod -a -G dialout,video $user - - #service start/stop may fail on non-OctoPi instances, but that is probably Ok - if [ -f "/home/$user/.octoprint/config.yaml" ]; then - if grep -q 'firstRun: false' /home/$user/.octoprint/config.yaml; then - echo "It looks as though this installation has already been in use." | log - echo "In order to use the script, the files must be moved." - echo "If you chose to continue with the installation these files will be moved (not erased)." - echo "They will be found at /home/$user/.old-octo" - echo "If you have generated service files for OctoPrint, please stop and disable them." - if prompt_confirm "Continue with installation?"; then - MOVE=1 - echo "Continuing installation." | log - systemctl stop octoprint.service - echo "Moving files to /home/$user/.old-octo" | log - mv /home/$user/.octoprint /home/$user/.old-octo - systemctl start octoprint.service - else - main_menu - fi - fi - fi - - if [ $INSTALL -eq 1 ]; then - OCTOEXEC="sudo -u $user /home/$user/oprint/bin/octoprint" - OCTOPIP="sudo -u $user /home/$user/oprint/bin/pip" - echo - echo - if prompt_confirm "Would you like to install and use ustreamer instead of mjpg-streamer?"; then - echo 'streamer: ustreamer' >> /etc/octoprint_deploy - apt-get -y install libevent-dev libbsd-dev - sudo -u $user git clone --depth=1 https://github.com/pikvm/ustreamer - sudo -u $user make -C ustreamer > /dev/null - else - echo 'streamer: mjpg-streamer' >> /etc/octoprint_deploy - fi - - echo 'Disabling unneeded services....' - systemctl disable octoprint.service - systemctl disable webcamd.service - systemctl stop webcamd.service - systemctl disable streamer_select.service - systemctl stop streamer_select.service - echo 'Installing needed packages' - apt-get -y install uuid-runtime - echo "Adding systemctl and reboot to sudo" - echo "$user ALL=NOPASSWD: /usr/bin/systemctl" > /etc/sudoers.d/octoprint_systemctl - echo "$user ALL=NOPASSWD: /usr/sbin/reboot" > /etc/sudoers.d/octoprint_reboot - echo 'haproxy: true' >> /etc/octoprint_deploy - echo 'Modifying config.yaml' - cp -p $SCRIPTDIR/config.basic /home/$user/.octoprint/config.yaml - firstrun - echo 'Connect to your octoprint (octopi.local) instance and setup admin user if you have not already' - echo 'type: octopi' >> /etc/octoprint_deploy - echo - echo - if prompt_confirm "Would you like to install recommended plugins now?"; then - plugin_menu - fi - echo - echo - if prompt_confirm "Would you like to install cloud service plugins now?"; then - plugin_menu_cloud - fi - systemctl restart octoprint.service - - fi - - if [ $INSTALL -gt 1 ]; then - OCTOEXEC="sudo -u $user /home/$user/OctoPrint/bin/octoprint" - OCTOPIP="sudo -u $user /home/$user/OctoPrint/bin/pip" - echo "Adding systemctl and reboot to sudo" - echo "$user ALL=NOPASSWD: /usr/bin/systemctl" > /etc/sudoers.d/octoprint_systemctl - echo "$user ALL=NOPASSWD: /usr/sbin/reboot" > /etc/sudoers.d/octoprint_reboot - echo "This will install necessary packages, download and install OctoPrint and setup a template instance on this machine." - #install packages - #All DEB based - #Python 3.11 currently not compatible with OP, redefine for Fedora - PYVERSION="python3" - if [ $INSTALL -eq 2 ]; then - apt-get update > /dev/null - deb_packages - fi - #Fedora35/CentOS - if [ $INSTALL -eq 3 ]; then - dnf -y install gcc python3-devel cmake libjpeg-turbo-devel libbsd-devel libevent-devel haproxy openssh openssh-server libffi-devel - systemctl enable sshd.service - PYV=$(python3 -c"import sys; print(sys.version_info.minor)") - if [ $PYV -eq 11 ]; then - dnf -y install python3.10-devel - PYVERSION='python3.10' - fi - fi - - #ArchLinux - if [ $INSTALL -eq 4 ]; then - pacman -S --noconfirm make cmake python python-virtualenv libyamlpython-pip libjpeg-turbo python-yaml python-setuptools libffi ffmpeg gcc libevent libbsd openssh haproxy v4l-utils - usermod -a -G uucp $user - fi - echo "Enabling ssh server..." - systemctl enable ssh.service - echo "Installing OctoPrint virtual environment in /home/$user/OctoPrint" - #make venv - sudo -u $user $PYVERSION -m venv /home/$user/OctoPrint - #update pip - sudo -u $user /home/$user/OctoPrint/bin/pip install --upgrade pip - #pre-install wheel - sudo -u $user /home/$user/OctoPrint/bin/pip install wheel - #install oprint - sudo -u $user /home/$user/OctoPrint/bin/pip install OctoPrint - - #NEW! Do check to verify that OctoPrint binary is installed - if [ -f "/home/$user/OctoPrint/bin/octoprint" ]; then - echo "OctoPrint apppears to have been installed successfully" - else - echo "WARNING! WARNING! WARNING!" - echo "OctoPrint has not been installed correctly." - echo "Please answer Y to remove everything and try running prepare system again." - remove_everything - exit - fi - - #start server and run in background - echo 'Creating generic OctoPrint template service...' - cat $SCRIPTDIR/octoprint_generic.service | \ - sed -e "s/OCTOUSER/$user/" \ - -e "s#OCTOPATH#/home/$user/OctoPrint/bin/octoprint#" \ - -e "s#OCTOCONFIG#/home/$user/#" \ - -e "s/NEWINSTANCE/octoprint/" \ - -e "s/NEWPORT/5000/" > /etc/systemd/system/octoprint_default.service - echo 'Updating config.yaml' - sudo -u $user mkdir /home/$user/.octoprint - sudo -u $user cp -p $SCRIPTDIR/config.basic /home/$user/.octoprint/config.yaml - #Haproxy - echo - echo - echo 'You have the option of setting up haproxy.' - echo 'This binds instances to a name on port 80 instead of having to type the port.' - echo - echo - if prompt_confirm "Use haproxy?"; then - echo 'haproxy: true' >> /etc/octoprint_deploy - #Check if using improved haproxy rules - echo 'haproxynew: true' >> /etc/octoprint_deploy - systemctl stop haproxy - #get haproxy version - HAversion=$(haproxy -v | sed -n 's/^.*version \([0-9]\).*/\1/p') - mv /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.orig - if [ $HAversion -gt 1 ]; then - cp $SCRIPTDIR/haproxy2x.basic /etc/haproxy/haproxy.cfg - else - cp $SCRIPTDIR/haproxy1x.basic /etc/haproxy/haproxy.cfg - fi - systemctl start haproxy - systemctl enable haproxy - else - systemctl stop haproxy - systemctl disable haproxy - fi - - echo - echo - echo - - streamer_install - - #Fedora has SELinux on by default so must make adjustments? Don't really know what these do... - if [ $INSTALL -eq 3 ]; then - semanage fcontext -a -t bin_t "/home/$user/OctoPrint/bin/.*" - chcon -Rv -u system_u -t bin_t "/home/$user/OctoPrint/bin/" - restorecon -R -v /home/$user/OctoPrint/bin - if [ $VID -eq 1 ]; then - semanage fcontext -a -t bin_t "/home/$user/mjpg-streamer/.*" - chcon -Rv -u system_u -t bin_t "/home/$user/mjpg-streamer/" - restorecon -R -v /home/$user/mjpg-streamer - fi - if [ $VID -eq 2 ]; then - semanage fcontext -a -t bin_t "/home/$user/ustreamer/.*" - chcon -Rv -u system_u -t bin_t "/home/$user/ustreamer/" - restorecon -R -v /home/$user/ustreamer - fi - - fi - - #Prompt for admin user and firstrun stuff - firstrun - echo 'type: linux' >> /etc/octoprint_deploy - echo 'Starting template service on port 5000' - echo -e "\033[0;31mConnect to your template instance and setup the admin user if you have not done so already.\033[0m" - systemctl start octoprint_default.service - systemctl enable octoprint_default.service - echo - echo - if prompt_confirm "Would you like to install recommended plugins now?"; then - plugin_menu - fi - echo - echo - #if prompt_confirm "Would you like to install cloud service plugins now?"; then - # plugin_menu_cloud - #fi - #this restart seems necessary in some cases - systemctl restart octoprint_default.service - fi - echo 'instance:generic port:5000' > /etc/octoprint_instances - touch /etc/octoprint_instances - echo 'Adding camera port records' - touch /etc/camera_ports - if [ $MOVE -eq 1 ]; then - echo "You can move your previously uploaded gcode to the template instance now." - echo "If you do this, ALL new instances will have these gcode files." - if prompt_confirm "Move old gcode files to template instance?"; then - mv /home/$user/.old-octo/uploads /home/$user/.octoprint/uploads - fi - fi - echo "System preparation complete!" - - fi - main_menu -} - -streamer_install() { - PS3='Which video streamer you would like to install?: ' - options=("mjpeg-streamer" "ustreamer (recommended)" "None") - select opt in "${options[@]}" - do - case $opt in - "mjpeg-streamer") - VID=1 - break - ;; - "ustreamer (recommended)") - VID=2 - break - ;; - "None") - VID=3 - break - ;; - *) echo "invalid option $REPLY";; - esac - done - - if [ $VID -eq 1 ]; then - - #install mjpg-streamer, not doing any error checking or anything - echo 'Installing mjpeg-streamer' - sudo -u $user git clone https://github.com/jacksonliam/mjpg-streamer.git mjpeg - #apt -y install - sudo -u $user make -C mjpeg/mjpg-streamer-experimental > /dev/null - sudo -u $user mv mjpeg/mjpg-streamer-experimental /home/$user/mjpg-streamer - sudo -u $user rm -rf mjpeg - - if [ -f "/home/$user/mjpg-streamer/mjpg_streamer" ]; then - echo "mjpg_streamer installed successfully" - echo 'streamer: mjpg-streamer' >> /etc/octoprint_deploy - else - echo "There was a problem installing the streamer. Please try again." - streamer_install - fi - fi - - if [ $VID -eq 2 ]; then - - #install ustreamer - sudo -u $user git clone --depth=1 https://github.com/pikvm/ustreamer - sudo -u $user make -C ustreamer > /dev/null - - if [ -f "/home/$user/ustreamer/ustreamer" ]; then - echo "ustreamer installed successfully" - echo 'streamer: ustreamer' >> /etc/octoprint_deploy - else - echo "There was a problem installing the streamer. Please try again." - streamer_install - fi - fi - - if [ $VID -eq 3 ]; then - echo "Good for you! Cameras are just annoying anyway." - fi -} - -firstrun() { - echo - echo - echo 'The template instance can be configured at this time.' - echo 'This includes setting up the admin user and finishing the startup wizards.' - echo 'If you do these now, you will not have to connect to the template with a browser.' - echo - echo - if prompt_confirm "Do you want to setup your admin user now?"; then - while true; do - echo 'Enter admin user name (no spaces): ' - read OCTOADMIN - if [ -z "$OCTOADMIN" ]; then - echo -e "No admin user given! Defaulting to: \033[0;31moctoadmin\033[0m" - OCTOADMIN=octoadmin - fi - if ! has-space "$OCTOADMIN"; then - break - else - echo "Admin user name must not have spaces." - fi - done - echo "Admin user: $OCTOADMIN" - - while true; do - echo 'Enter admin user password (no spaces): ' - read OCTOPASS - if [ -z "$OCTOPASS" ]; then - echo -e "No password given! Defaulting to: \033[0;31mfooselrulz\033[0m. Please CHANGE this." - OCTOPASS=fooselrulz - fi - - if ! has-space "$OCTOPASS"; then - break - else - echo "Admin password cannot contain spaces" - fi - - done - echo "Admin password: $OCTOPASS" - $OCTOEXEC user add $OCTOADMIN --password $OCTOPASS --admin | log - - fi - if [ -n "$OCTOADMIN" ]; then - echo - echo - echo "The script can complete the first run wizards now. For more information on these, see the OctoPrint website." - echo "It is standard to accept these, as no identifying information is exposed through their usage." - echo - echo - if prompt_confirm "Do first run wizards now?"; then - $OCTOEXEC config set server.firstRun false --bool | log - $OCTOEXEC config set server.seenWizards.backup null | log - $OCTOEXEC config set server.seenWizards.corewizard 4 --int | log - - if prompt_confirm "Enable online connectivity check?"; then - $OCTOEXEC config set server.onlineCheck.enabled true --bool - else - $OCTOEXEC config set server.onlineCheck.enabled false --bool - fi - - if prompt_confirm "Enable plugin blacklisting?"; then - $OCTOEXEC config set server.pluginBlacklist.enabled true --bool - else - $OCTOEXEC config set server.pluginBlacklist.enabled false --bool - fi - - if prompt_confirm "Enable anonymous usage tracking?"; then - $OCTOEXEC config set plugins.tracking.enabled true --bool - else - $OCTOEXEC config set plugins.tracking.enabled false --bool - fi - - if prompt_confirm "Use default printer (can be changed later)?"; then - $OCTOEXEC config set printerProfiles.default _default - fi - fi - fi - -} - - -check_sn() { - if [ -f "/etc/udev/rules.d/99-octoprint.rules" ]; then - if grep -q $1 /etc/udev/rules.d/99-octoprint.rules; then - echo "An identical serial number has been detected in the udev rules. Please be warned, this will likely cause instability!" | log - else - echo "No duplicate serial number detected" | log - fi - fi -} -remove_instance() { - opt=$1 - #disable and remove service file - if [ -f /etc/systemd/system/$opt.service ]; then - systemctl stop $opt.service - systemctl disable $opt.service - rm /etc/systemd/system/$opt.service - fi - - #Get all cameras associated with this instance - readarray -t cameras < <(ls -1 /etc/systemd/system/cam*_$opt.service | sed -n -e 's/^.*\/\(.*\).service/\1/p') - for camera in "${cameras[@]}"; do - remove_camera $camera - done - - #remove udev entry - sed -i "/$opt/d" /etc/udev/rules.d/99-octoprint.rules - #remove files - rm -rf /home/$user/.$opt - #remove from octoprint_instances - sed -i "/$opt/d" /etc/octoprint_instances - #remove haproxy entry - if [ "$HAPROXY" == true ]; then - sed -i "/use_backend $opt/d" /etc/haproxy/haproxy.cfg - sed -i "/#$opt start/,/#$opt stop/d" /etc/haproxy/haproxy.cfg - systemctl restart haproxy.service - fi - -} - -remove_instance_menu() { - echo - echo - get_settings - if [ $SUDO_USER ]; then user=$SUDO_USER; fi - if [ -f "/etc/octoprint_instances" ]; then - - PS3='Select instance number to remove: ' - readarray -t options < <(cat /etc/octoprint_instances | sed -n -e 's/^instance:\([[:graph:]]*\) port:.*/\1/p') - options+=("Quit") - unset 'options[0]' - select opt in "${options[@]}" - do - if [ "$opt" == Quit ]; then - main_menu - fi - echo "Selected instance to remove: $opt" | log - break - done - - if prompt_confirm "Do you want to remove everything associated with this instance?"; then - remove_instance $opt - fi - fi - main_menu -} -remove_camera() { - systemctl stop $1.service - systemctl disable $1.service - rm /etc/systemd/system/$1.service - sed -i "/$1/d" /etc/udev/rules.d/99-octoprint.rules - if [ "$HAPROXY" == true ]; then - sed -i "/use_backend $1/d" /etc/haproxy/haproxy.cfg - sed -i "/#$1 start/,/#$1 stop/d" /etc/haproxy/haproxy.cfg - systemctl restart haproxy - fi -} - -remove_camera_menu() { - get_settings - #must choose where to find which cameras have been installed - #probably safest to go with service files - PS3='Select camera number to remove: ' - readarray -t cameras < <(ls -1 /etc/systemd/system/cam*.service | sed -n -e 's/^.*\/\(.*\).service/\1/p') - cameras+=("Quit") - - select camera in "${cameras[@]}" - do - if [ "$camera" == Quit ]; then - main_menu - fi - - echo "Removing udev, service files, and haproxy entry for $camera" | log - remove_camera $camera - main_menu - done -} - -remove_everything() { - get_settings - if prompt_confirm "Remove everything?"; then - readarray -t instances < <(cat /etc/octoprint_instances | sed -n -e 's/^instance:\([[:graph:]]*\) .*/\1/p') - unset 'instances[0]' - readarray -t cameras < <(ls -1 /etc/systemd/system/cam*.service | sed -n -e 's/^.*\/\(.*\).service/\1/p') - for instance in "${instances[@]}"; do - remove_instance $instance - done - - for camera in "${cameras[@]}"; do - remove_camera $camera - done - - echo "Removing system stuff" - rm /etc/systemd/system/octoprint_default.service - rm /etc/octoprint_streamer - rm /etc/octoprint_deploy - rm /etc/octoprint_instances - rm /etc/camera_ports - rm /etc/udev/rules.d/99-octoprint.rules - rm /etc/sudoers.d/octoprint_reboot - rm /etc/sudoers.d/octoprint_systemctl - echo "Removing template" - rm -rf /home/$user/.octoprint - rm -rf /home/$user/OctoPrint - rm -rf /home/$user/ustreamer - rm -rf /home/$user/mjpg-streamer - systemctl restart haproxy.service - systemctl daemon-reload - - #if using OctoPi, restart template - if [ "$TYPE" == octopi ]; then - systemctl restart octoprint.service - fi - fi -} -utility_menu() { - echo - echo - PS3='Select an option: ' - options=("Instance Status" "USB Port Testing" "Sync Users" "Share Uploads" "Quit") - select opt in "${options[@]}" - do - case $opt in - "Instance Status") - instance_status - break - ;; - "USB Port Testing") - usb_testing - break - break - ;; - "Sync Users") - sync_users - break - ;; - "Share Uploads") - share_uploads - break - ;; - "Quit") - main_menu - break - ;;*) echo "invalid option $REPLY";; - esac - done -} - -backup_menu() { - echo - echo - PS3='Select an option: ' - options=("Create Backup" "Restore Backup" "Quit") - select opt in "${options[@]}" - do - case $opt in - "Create Backup") - create_menu - break - ;; - "Restore Backup") - restore_menu - break - break - ;; - "Quit") - main_menu - break - ;;*) echo "invalid option $REPLY";; - esac - done -} - -create_menu() { - echo - echo - PS3='Select instance number to backup: ' - readarray -t options < <(cat /etc/octoprint_instances | sed -n -e 's/^instance:\([[:graph:]]*\) .*/\1/p') - options+=("Quit") - select opt in "${options[@]}" - do - if [ "$opt" == Quit ]; then - main_menu - fi - - echo "Selected instance to backup: $opt" | log - back_up $opt - main_menu - done -} - -restart_all() { - get_settings - readarray -t instances < <(cat /etc/octoprint_instances | sed -n -e 's/^instance:\([[:graph:]]*\) .*/\1/p') - unset 'instances[0]' - for instance in "${instances[@]}"; do - if [ "$instance" == generic ]; then - continue - fi - echo "Trying to restart instance $instance" - systemctl restart $instance - done - exit 0 -} - -sync_users() { - echo - echo - echo "This will sync all the users from one instance to all the other instances." - PS3='Select instance that contains current user list: ' - readarray -t options < <(cat /etc/octoprint_instances | sed -n -e 's/^instance:\([[:graph:]]*\) .*/\1/p') - options+=("Quit") - select opt in "${options[@]}" - do - if [ "$opt" == Quit ]; then - main_menu - fi - - if prompt_confirm "Copy users from instance $opt to all other instances?"; then - if [ "$opt" == generic ]; then - userfile=/home/$user/.octoprint/users.yaml - else - userfile=/home/$user/.$opt/users.yaml - fi - #re-read to avoid the Quit - readarray -t instances < <(cat /etc/octoprint_instances | sed -n -e 's/^instance:\([[:graph:]]*\) .*/\1/p') - for instance in "${instances[@]}"; do - if [ "$instance" == "$opt" ]; then - continue - fi - if [ "$instance" == generic ]; then - sudo -u $user cp $userfile /home/$user/.octoprint/ - else - sudo -u $user cp $userfile /home/$user/.$instance/ - fi - done - - if prompt_confirm "Restart all instances now for changes to take effect?"; then - restart_all - fi - fi - - main_menu - done -} - -share_uploads() { - get_settings - echo - echo - echo "This option will make all your uploads go to a single instance." - echo "This will mean all gcode files be available for all your instances." - echo "Use this option only if you understand the implications." - echo "This can be adjusted later in the Folders settings of OctoPrint." - PS3='Select instance where uploads will be stored: ' - readarray -t options < <(cat /etc/octoprint_instances | sed -n -e 's/^instance:\([[:graph:]]*\) port:.*/\1/p') - options+=("Custom" "Quit") - unset 'options[0]' - select opt in "${options[@]}" - do - if [ "$opt" == Quit ]; then - main_menu - break - fi - - if [ "$opt" == "Custom" ]; then - echo "Enter full path (should start /home/$user/):" - read ULPATH - if [ -d "$ULPATH" ]; then - #echo "This folder already exists. Are you sure you want to use it?" - if prompt_confirm "This folder already exists. Are you sure you want to use it?"; then - opt=$ULPATH - else - echo "Restart the option if you change your mind" - main_menu - break - fi - else - sudo -u $user mkdir $ULPATH - opt=$ULPATH - fi - - else - opt=/home/$user/.$opt/uploads - fi - echo $opt - echo - #Remove Quit and Custom from array, is there a cleaner way? - unset 'options[-1]' - unset 'options[-1]' - for instance in "${options[@]}"; do - $OCTOEXEC --basedir /home/$user/.$instance config set folder.uploads "$opt" - done - break - done - echo "Instances must be restarted for changes to take effect." - main_menu -} - -back_up() { - INSTANCE=$1 - echo "Creating backup of $INSTANCE...." - if [ "$INSTANCE" == generic ]; then - INSTANCE="octoprint" - fi - d=$(date '+%Y-%m-%d') - sudo -p $user tar -czf ${INSTANCE}_${d}_backup.tar.gz -C /home/$user/ .${INSTANCE} - echo "Tarred and gzipped backup created in /home/$user" -} - -restore_menu() { - echo - echo - PS3='Select backup to restore: ' - readarray -t options < <(ls /home/$user/*.tar.gz) - options+=("Quit") - select opt in "${options[@]}" - do - if [ "$opt" == Quit ] || [ "$opt" == generic ]; then - main_menu - fi - - echo "Selected $opt to restore" | log - tar --same-owner -pxvf $opt - main_menu - done -} - -restore() { - INSTANCE=$1 - TAR=$2 - echo "Restoring backup of $INSTANCE...." - systemctl stop $INSTANCE - sudo -p $user tar -xvf $TAR - systemctl start $INSTANCE - -} - -back_up_all() { - get_settings - readarray -t instances < <(cat /etc/octoprint_instances | sed -n -e 's/^instance:\([[:graph:]]*\) .*/\1/p') - unset 'instances[0]' - for instance in "${instances[@]}"; do - echo $instance - back_up $instance - done - -} - -#Get current udev identification for an instance, replace via auto-detect -replace_id() { - echo "PLEASE NOTE, this will only work in replacing an existing serial number with another serial number" - echo "or an existing USB port with another USB port. You cannot mix and match." - PS3='Select instance to change serial ID: ' - readarray -t options < <(cat /etc/octoprint_instances | sed -n -e 's/^instance:\([[:graph:]]*\) .*/\1/p') - options+=("Quit") - unset 'options[0]' - select opt in "${options[@]}" - do - if [ "$opt" == Quit ]; then - main_menu - fi - - echo "Selected $opt to replace serial ID" | log - #Serial number or KERNELS? Not doing any error checking yet - KERN=$(grep octo_$opt /etc/udev/rules.d/99-octoprint.rules | sed -n -e 's/KERNELS==\"\([[:graph:]]*[[:digit:]]\)\".*/\1/p') - detect_printer - if [ -z "$KERN" ]; then - sed -i -e "s/\(ATTRS{serial}==\)\"\([[:alnum:]]*\)\"\(.*\)\(\"octo_$opt\"\)/\1\"$UDEV\"\2\3/" /etc/udev/rules.d/99-octoprint.rules - echo "Serial number replaced with: $UDEV" - else - sed -i -e "s/\(KERNELS==\)\"$KERN\"\(.*\)\(\"octo_$opt\"\)/\1\"$USB\"\2\3/" /etc/udev/rules.d/99-octoprint.rules - echo "USB port replaced with: $USB" - fi - udevadm control --reload-rules - udevadm trigger - exit 0 - done -} - -octo_deploy_update() { - sudo -u $user git -C $SCRIPTDIR pull - exit -} - -instance_status() { - echo - echo "*******************************************" - readarray -t instances < <(cat /etc/octoprint_instances | sed -n -e 's/^instance:\([[:graph:]]*\) .*/\1/p') - unset 'instances[0]' - echo "Instance - Status:" - echo "------------------" - for instance in "${instances[@]}"; do - status=$(systemctl status $instance | sed -n -e 's/Active: \([[:graph:]]*\) .*/\1/p') - echo "$instance - $status" - done - echo "*******************************************" - main_menu -} - -main_menu() { - VERSION=0.2.5 - #reset - UDEV='' - TEMPUSB='' - CAM='' - TEMPUSBCAM='' - INSTANCE='' - INSTALL='' - CAMHAPROXY='' - echo - echo - echo "*************************" - echo "octoprint_deploy $VERSION" - echo "*************************" - echo - PS3='Select operation: ' - if [ -f "/etc/octoprint_instances" ]; then - options=("New instance" "Delete instance" "Add Camera" "Delete Camera" "Utilities" "Backup Menu" "Update" "Quit") - else - options=("Prepare system" "USB port testing" "Update" "Quit") - fi - - select opt in "${options[@]}" - do - case $opt in - "Prepare system") - prepare - break - ;; - "New instance") - new_instance - break - ;; - "Delete instance") - remove_instance_menu - break - ;; - "Add Camera") - add_camera - break - ;; - "Delete Camera") - remove_camera_menu - break - ;; - "Utilities") - utility_menu - break - ;; - "Backup Menu") - backup_menu - break - ;; - "USB port testing") - usb_testing - break - ;; - "Update") - octo_deploy_update - break - ;; - "Quit") - exit 1 - ;; - *) echo "invalid option $REPLY";; - esac - done -} # initiate logging logfile='octoprint_deploy.log' SCRIPTDIR=$(dirname $(readlink -f $0)) source $SCRIPTDIR/plugins.sh -# gather info and write /etc/octoprint_deploy if missing -if [ ! -f /etc/octoprint_deploy ] && [ -f /etc/octoprint_instances ]; then - echo "/etc/octoprint_deploy is missing. You may have prepared the system with an older vesion." - echo "The file will be created now." - streamer_type=("mjpg-streamer" "ustreamer") - haproxy_bool=("true" "false") - if [ -f /etc/octopi_version ]; then - echo "type: octopi" >> /etc/octoprint_deploy - apt-get -y install uuid-runtime - else - echo "type: linux" >> /etc/octoprint_deploy - fi - PS3='Select streamer type: ' - select str in "${streamer_type[@]}"; do - echo "streamer: $str" >> /etc/octoprint_deploy - break - done - PS3='Using haproxy (select true if using octopi): ' - select prox in "${haproxy_bool[@]}"; do - echo "haproxy: $prox" >> /etc/octoprint_deploy - break - done - -fi +source $SCRIPTDIR/prepare.sh +source $SCRIPTDIR/instance.sh +source $SCRIPTDIR/util.sh +source $SCRIPTDIR/menu.sh +source $SCRIPTDIR/cameras.sh get_settings -#02/17/23 - This will upgrade haproxy so it will no longer require the trailling slash -if [ "$HAPROXYNEW" == false ] && [ "$HAPROXY" == true ]; then - #Update haproxy entries - echo "Detected older version of haproxy entries. Updating those now." - readarray -t instances < <(cat /etc/octoprint_instances | sed -n -e 's/^instance:\([[:graph:]]*\) .*/\1/p') - unset 'instances[0]' - for instance in "${instances[@]}"; do - sed -i "/use_backend $instance/d" /etc/haproxy/haproxy.cfg - SEDREPLACE="#$instance start\n\ - acl is_$instance url_beg /$instance\n\ - http-request redirect scheme http drop-query append-slash if is_$instance ! { path_beg /$instance/ }\n\ - use_backend $instance if { path_beg /$instance/ }\n\ - #$instance stop" - sed -i "/option forwardfor except 127.0.0.1/a $SEDREPLACE" /etc/haproxy/haproxy.cfg - done - echo 'haproxynew: true' >> /etc/octoprint_deploy - systemctl restart haproxy -fi - - #command line arguments if [ "$1" == remove ]; then remove_everything @@ -1626,10 +44,6 @@ if [ "$1" == backup ]; then back_up_all fi -if [ "$1" == replace ]; then - replace_id -fi - if [ "$1" == picam ]; then add_camera true fi @@ -1637,4 +51,8 @@ fi if [ "$1" == noserial ]; then NOSERIAL=1 fi + +if [ "$1" == deb_packages ]; then + deb_packages +fi main_menu diff --git a/plugins.sh b/plugins.sh index 008c882..fe555ee 100755 --- a/plugins.sh +++ b/plugins.sh @@ -1,14 +1,15 @@ #!/bin/bash install_plugin() { - echo "Installing $plugin...." | log + echo "Installing $plugin...." $OCTOPIP install "$plugin_path" } plugin_menu() { echo echo - PS3='Select recommended plugins to install: ' + get_settings + PS3="${green}Select recommended plugins to install: ${white}" readarray -t plugins < <(cat $SCRIPTDIR/plugins_list | sed -n -e 's/^plugin:\(.*\) path:.*/\1/p') plugins+=("All") plugins+=("Quit") @@ -37,21 +38,3 @@ plugin_menu() { } -plugin_menu_cloud() { - echo - echo "You can setup cloud-based plugins at this time. Some will have to be configured" - echo "in your template instance before making new instances." - echo - PS3='Select cloud-based plugins to install: ' - readarray -t plugins < <(cat $SCRIPTDIR/plugins_cloud | sed -n -e 's/^plugin:\(.*\) path:.*/\1/p') - plugins+=("Quit") - select plugin in "${plugins[@]}" - do - if [ "$plugin" == Quit ]; then - break - fi - plugin_path=$(cat $SCRIPTDIR/plugins_cloud | sed -n -e "s/^plugin:$plugin path:\([[:graph:]]*\)/\1/p") - install_plugin - break - done -} diff --git a/plugins_cloud b/plugins_cloud deleted file mode 100644 index 5b6b897..0000000 --- a/plugins_cloud +++ /dev/null @@ -1,4 +0,0 @@ -plugin:SimplyPrint Cloud path:https://github.com/SimplyPrint/OctoPrint-SimplyPrint/archive/master.zip -plugin:Polar Cloud path:https://github.com/markwal/OctoPrint-PolarCloud/archive/master.zip -plugin:AstroPrint Cloud path:https://github.com/AstroPrint/OctoPrint-AstroPrint/archive/master.zip -plugin:OctoEverywhere path:https://github.com/QuinnDamerell/OctoPrint-OctoEverywhere/archive/master.zip \ No newline at end of file diff --git a/plugins_list b/plugins_list index 07111af..ebebf13 100644 --- a/plugins_list +++ b/plugins_list @@ -4,4 +4,5 @@ plugin:Firmware Updater path:https://github.com/OctoPrint/OctoPrint-FirmwareUpda plugin:Terminal Commands Extended: path:https://github.com/jneilliii/OctoPrint-TerminalCommandsExtended/archive/master.zip plugin:Active Filters Extended path:https://github.com/jneilliii/OctoPrint-ActiveFiltersExtended/archive/master.zip plugin:PrintTimeGenius path:https://github.com/eyal0/OctoPrint-PrintTimeGenius/archive/master.zip -plugin:UI Customizer path:https://github.com/LazeMSS/OctoPrint-UICustomizer/archive/main.zip \ No newline at end of file +plugin:UI Customizer path:https://github.com/LazeMSS/OctoPrint-UICustomizer/archive/main.zip +plugin:PowerFailure path:https://github.com/pablogventura/OctoPrint-PowerFailure/archive/master.zip \ No newline at end of file diff --git a/prepare.sh b/prepare.sh new file mode 100644 index 0000000..5bdfc30 --- /dev/null +++ b/prepare.sh @@ -0,0 +1,533 @@ +#!/bin/bash + +detect_installs() { + #OctoPi will be the most common so do a search for that: + if [ -f "/etc/octopi_version" ]; then + echo "OctoPi installation detected." + echo "Adding standard OctoPi instance to instance list." + echo "instance:octoprint port:5000 udev:false" >> /etc/octoprint_instances + echo "octoexec: /home/$user/oprint/bin/octoprint" >> /etc/octoprint_deploy + echo "octopip: /home/$user/oprint/bin/pip" >> /etc/octoprint_deploy + echo "haproxy: true" >> /etc/octoprint_deploy + echo "octopi: true" >> /etc/octoprint_deploy + echo "Adding systemctl and reboot to sudo" + echo "$user ALL=NOPASSWD: /usr/bin/systemctl" > /etc/sudoers.d/octoprint_systemctl + echo "$user ALL=NOPASSWD: /usr/sbin/reboot" > /etc/sudoers.d/octoprint_reboot + INSTANCE=octoprint + deb_packages + #rename + + #detect + echo "If you plan to have multiple printers on your Pi it is helpful to assign printer udev rules." + echo "This will make sure the correct printer is associated with each OctoPrint instance." + echo "This can also be done in the Utility menu at a later time." + get_settings + if prompt_confirm "${green}Would you like to generate a udev rule now?{$white}"; then + echo "Unplug your printer from the USB connection now." + if prompt_confirm "${green}Ready to begin printer auto-detection?${white}"; then + detect_printer + printer_udev false + printer_udev true + udevadm control --reload-rules + udevadm trigger + sudo -u $user $OCTOEXEC config set serial.port /dev/octo_$INSTANCE + sudo -u $user $OCTOEXEC config append_value serial.additionalPorts "/dev/octo_$INSTANCE" + #convert UDEV to true + sed -i "s/udev:false/udev:true/" /etc/octoprint_instances + systemctl restart $INSTANCE + fi + + fi + #streamer_install + echo "streamer: camera-streamer" >> /etc/octoprint_deploy + main_menu + fi + + echo "Searching home directory for existing OctoPrint venv/binary....." + octopresent=$(find /home/$user/ -type f -executable -print | grep "bin/octoprint") + if [ -n "$octopresent" ]; then + echo "OctoPrint binary found at $octopresent" + PS3="${green}Select option number: ${white}" + options=("Use existing binary" "Install most recent OctoPrint" "More information") + select opt in "${options[@]}" + do + case $opt in + "Use existing binary") + OCTOEXEC=$octopresent + break + ;; + "Install most recent OctoPrint") + OCTOEXEC=thing + break + ;; + "More information") + exit 1 + ;; + *) echo "invalid option $REPLY";; + esac + done + else + echo "No OctoPrint binary found in the current user's home directory. Doing complete install." + FULLINSTALL=1 + fi + echo "Looking for existing OctoPrint systemd files....." + #get any service files that have bin/octoprint + readarray -t syspresent < <(fgrep -l bin/octoprint /etc/systemd/system/*.service) + prepare +} + +#https://askubuntu.com/questions/39497 +deb_packages() { + #All extra packages needed can be added here for deb based systems. Only available will be selected. + apt-cache --generate pkgnames \ + | grep --line-regexp --fixed-strings \ + -e make \ + -e v4l-utils \ + -e python-is-python3 \ + -e python3-venv \ + -e python3.9-venv \ + -e python3.10-venv \ + -e virtualenv \ + -e python3-dev \ + -e build-essential \ + -e python3-setuptools \ + -e libyaml-dev \ + -e python3-pip \ + -e cmake \ + -e libjpeg8-dev \ + -e libjpeg62-turbo-dev \ + -e gcc \ + -e g++ \ + -e libevent-dev \ + -e libjpeg-dev \ + -e libbsd-dev \ + -e ffmpeg \ + -e uuid-runtime\ + -e ssh\ + -e libffi-dev\ + -e haproxy\ + -e libavformat-dev\ + -e libavutil-dev\ + -e libavcodec-dev\ + -e libcamera-dev\ + -e liblivemedia-dev\ + -e v4l-utils\ + -e pkg-config\ + -e xxd\ + -e build-essential\ + -e libssl-dev\ + -e rsync\ + | xargs apt-get install -y + + #pacakges to REMOVE go here + apt-cache --generate pkgnames \ + | grep --line-regexp --fixed-strings \ + -e brltty \ + | xargs apt-get remove -y + +} + +dnf_packages() { + #untested + dnf install -y \ + gcc \ + python3-devel \ + cmake \ + libjpeg-turbo-devel \ + libbsd-devel \ + libevent-devel \ + haproxy \ + openssh \ + openssh-server \ + libffi-devel \ + libcamera-devel \ + v4l-utils \ + xxd \ + openssl-devel \ + rsync + +} + +pacman_packages() { + pacman -S --noconfirm \ + make \ + cmake \ + python \ + python-virtualenv \ + libyaml \ + python-pip \ + libjpeg-turbo \ + python-yaml \ + python-setuptools \ + libffi \ + ffmpeg \ + gcc \ + libevent \ + libbsd \ + openssh \ + haproxy \ + v4l-utils \ + rsync +} + +zypper_packages() { + zypper in -y \ + gcc \ + python3-devel \ + cmake \ + libjpeg-devel \ + libbsd-devel \ + libevent-devel \ + haproxy \ + openssh \ + openssh-server \ + libffi-devel \ + v4l-utils \ + xxd \ + libopenssl-devel \ + rsync + +} + +user_groups() { + + echo 'Adding current user to dialout and video groups.' + usermod -a -G dialout,video $user + + if [ $INSTALL -eq 4 ]; then + usermod -a -G uucp $user + fi +} + +prepare () { + echo + echo + + PS3="${green}Installation type: ${white}" + local options=("Ubuntu 20+, Mint, Debian, Raspberry Pi OS" "Fedora/CentOS" "ArchLinux" "openSUSE" "Quit") + select opt in "${options[@]}" + do + case $opt in + "Ubuntu 20+, Mint, Debian, Raspberry Pi OS") + INSTALL=2 + break + ;; + "Fedora/CentOS") + INSTALL=3 + break + ;; + "ArchLinux") + INSTALL=4 + break + ;; + "openSUSE") + INSTALL=5 + break + ;; + "Quit") + exit 1 + ;; + *) echo "invalid option $REPLY";; + esac + done + + if prompt_confirm "Ready to begin?"; then + + #CHOICE HERE between new_install or directly to haproxy/streamer based on if existing binary is used + if [ -n "$FULLINSTALL" ]; then + new_install + else + old_install + fi + + fi + main_menu +} + +old_install() { + echo "octoexec:$octopresent" >> /etc/octoprint_deploy + haproxy_install + streamer_install + #add existing instance(s) to /etc/octoprint_instances +} + +new_install() { + OCTOEXEC=/home/$user/OctoPrint/bin/octoprint + echo "Adding systemctl and reboot to sudo" + echo "$user ALL=NOPASSWD: /usr/bin/systemctl" > /etc/sudoers.d/octoprint_systemctl + echo "$user ALL=NOPASSWD: /usr/sbin/reboot" > /etc/sudoers.d/octoprint_reboot + echo "This will install necessary packages, install OctoPrint and setup an instance." + #install packages + #All DEB based + PYVERSION="python3" + if [ $INSTALL -eq 2 ]; then + apt-get update > /dev/null + deb_packages + fi + + #Fedora35/CentOS + if [ $INSTALL -eq 3 ]; then + echo "Fedora and variants have SELinux enabled by default." + echo "This causes a fair bit of trouble for running OctoPrint." + echo "You have the option of disabling this now." + if prompt_confirm "${green}Disable SELinux?${white}"; then + sed -i s/^SELINUX=.*$/SELINUX=disabled/ /etc/selinux/config + echo "${magenta}You will need to reboot after system preparation.${white}" + fi + systemctl enable sshd.service + PYV=$(python3 -c"import sys; print(sys.version_info.minor)") + if [ $PYV -eq 11 ]; then + dnf -y install python3.10-devel + PYVERSION='python3.10' + fi + dnf_packages + fi + + #ArchLinux + if [ $INSTALL -eq 4 ]; then + pacman_packages + fi + + if [ $INSTALL -eq 5 ]; then + zypper_packages + systemctl enable sshd.service + fi + + echo "Enabling ssh server..." + systemctl enable ssh.service + echo "Installing OctoPrint virtual environment in /home/$user/OctoPrint" + #make venv + sudo -u $user $PYVERSION -m venv /home/$user/OctoPrint + #update pip + sudo -u $user /home/$user/OctoPrint/bin/pip install --upgrade pip + #pre-install wheel + sudo -u $user /home/$user/OctoPrint/bin/pip install wheel + #install oprint + sudo -u $user /home/$user/OctoPrint/bin/pip install OctoPrint + + #Check to verify that OctoPrint binary is installed + if [ -f "/home/$user/OctoPrint/bin/octoprint" ]; then + echo "${cyan}OctoPrint apppears to have been installed successfully${white}" + else + echo "${red}WARNING! WARNING! WARNING!${white}" + echo "OctoPrint has not been installed correctly." + echo "Please answer Y to remove everything and try running prepare system again." + remove_everything + exit + fi + + haproxy_install + streamer_install + + #These will retreived as settings + echo "octoexec: /home/$user/OctoPrint/bin/octoprint" >> /etc/octoprint_deploy + echo "octopip: /home/$user/OctoPrint/bin/pip" >> /etc/octoprint_deploy + + #Create first instance + echo + echo + echo + echo + echo "${cyan}It is time to create your first OctoPrint instance!!!${white}" + new_instance true + echo + echo + if prompt_confirm "Would you like to install recommended plugins now?"; then + plugin_menu + fi + +} + +haproxy_install() { + + echo + echo + echo 'You have the option of setting up haproxy.' + echo 'This binds instances to a name on port 80 instead of having to type the port.' + echo + echo + if prompt_confirm "Use haproxy?"; then + echo 'haproxy: true' >> /etc/octoprint_deploy + #Check if using improved haproxy rules + #echo 'haproxynew: true' >> /etc/octoprint_deploy + systemctl stop haproxy + #get haproxy version + HAversion=$(haproxy -v | sed -n 's/^.*version \([0-9]\).*/\1/p') + mv /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.orig + if [ $HAversion -gt 1 ]; then + cp $SCRIPTDIR/haproxy2x.basic /etc/haproxy/haproxy.cfg + else + cp $SCRIPTDIR/haproxy1x.basic /etc/haproxy/haproxy.cfg + fi + systemctl start haproxy + systemctl enable haproxy + else + systemctl stop haproxy + systemctl disable haproxy + fi + + echo + echo + echo +} + +streamer_install() { + PS3="${green}Which video streamer you would like to install?: ${white}" + options=("ustreamer (recommended)" "None/Skip") + select opt in "${options[@]}" + do + case $opt in + "mjpeg-streamer") + VID=1 + break + ;; + "ustreamer (recommended)") + VID=2 + break + ;; + "camera-streamer") + VID=4 + break + ;; + "None/Skip") + VID=3 + break + ;; + *) echo "invalid option $REPLY";; + esac + done + + #If we run this function directly, clean up streamer setting before installing + get_settings + if [ -n "$STREAMER" ]; then + sed -i "/$STREAMER/d" /etc/octoprint_deploy + fi + + if [ $VID -eq 1 ]; then + + #install mjpg-streamer, not doing any error checking or anything + echo 'Installing mjpeg-streamer' + sudo -u $user git -C /home/$user/ clone https://github.com/jacksonliam/mjpg-streamer.git mjpeg + #apt -y install + sudo -u $user make -C /home/$user/mjpeg/mjpg-streamer-experimental > /dev/null + + sudo -u $user mv /home/$user/mjpeg/mjpg-streamer-experimental /home/$user/mjpg-streamer + sudo -u $user rm -rf /home/$user/mjpeg + if [ -f "/home/$user/mjpg-streamer/mjpg_streamer" ]; then + echo "Streamer installed successfully" + else + echo "${red}WARNING! WARNING! WARNING!${white}" + echo "Streamer has not been installed correctly." + if prompt_confirm "Try installation again?"; then + streamer_install + fi + fi + echo 'streamer: mjpg-streamer' >> /etc/octoprint_deploy + fi + + if [ $VID -eq 2 ]; then + + #install ustreamer + sudo -u $user git -C /home/$user clone --depth=1 https://github.com/pikvm/ustreamer + sudo -u $user make -C /home/$user/ustreamer > /dev/null + if [ -f "/home/$user/ustreamer/ustreamer" ]; then + echo "Streamer installed successfully" + else + echo "${red}WARNING! WARNING! WARNING!${white}" + echo "Streamer has not been installed correctly." + if prompt_confirm "Try installation again?"; then + streamer_install + fi + fi + echo 'streamer: ustreamer' >> /etc/octoprint_deploy + fi + + if [ $VID -eq 4 ]; then + + #install camera-streamer + sudo -u $user git -C /home/$user clone https://github.com/ayufan-research/camera-streamer.git --recursive + sudo -u $user make -C /home/$user/camera-streamer > /dev/null + if [ -f "/home/$user/camera-streamer/camera-streamer" ]; then + echo "Streamer installed successfully" + else + echo "${red}WARNING! WARNING! WARNING!${white}" + echo "Streamer has not been installed correctly." + if prompt_confirm "Try installation again?"; then + streamer_install + fi + fi + echo 'streamer: camera-streamer' >> /etc/octoprint_deploy + fi + + if [ $VID -eq 3 ]; then + echo 'streamer: none' >> /etc/octoprint_deploy + echo "Good for you! Cameras are just annoying anyway." + fi + +} + +firstrun_install() { + echo + echo + echo 'The first instance can be configured at this time.' + echo 'This includes setting up the admin user and finishing the startup wizards.' + echo + echo + if prompt_confirm "Do you want to setup your admin user now?"; then + while true; do + echo 'Enter admin user name (no spaces): ' + read OCTOADMIN + if [ -z "$OCTOADMIN" ]; then + echo -e "No admin user given! Defaulting to: \033[0;31moctoadmin\033[0m" + OCTOADMIN=octoadmin + fi + if ! has-space "$OCTOADMIN"; then + break + else + echo "Admin user name must not have spaces." + fi + done + echo "Admin user: ${cyan}$OCTOADMIN${white}" + + while true; do + echo 'Enter admin user password (no spaces): ' + read OCTOPASS + if [ -z "$OCTOPASS" ]; then + echo -e "No password given! Defaulting to: ${cyan}fooselrulz${white}. Please CHANGE this." + OCTOPASS=fooselrulz + fi + + if ! has-space "$OCTOPASS"; then + break + else + echo "Admin password cannot contain spaces" + fi + + done + echo "Admin password: ${cyan}$OCTOPASS${white}" + sudo -u $user $OCTOEXEC --basedir $BASE user add $OCTOADMIN --password $OCTOPASS --admin + fi + + if [ -n "$OCTOADMIN" ]; then + echo + echo + echo "The script can complete the first run wizards now." + echo "For more information on these, see the OctoPrint website." + echo "It is standard to accept this, as no identifying information is exposed through their usage." + echo + echo + if prompt_confirm "Complete first run wizards now?"; then + sudo -u $user $OCTOEXEC --basedir $BASE config set server.firstRun false --bool + sudo -u $user $OCTOEXEC --basedir $BASE config set server.seenWizards.backup null + sudo -u $user $OCTOEXEC --basedir $BASE config set server.seenWizards.corewizard 4 --int + sudo -u $user $OCTOEXEC --basedir $BASE config set server.onlineCheck.enabled true --bool + sudo -u $user $OCTOEXEC --basedir $BASE config set server.pluginBlacklist.enabled true --bool + sudo -u $user $OCTOEXEC --basedir $BASE config set plugins.tracking.enabled true --bool + sudo -u $user $OCTOEXEC --basedir $BASE config set printerProfiles.default _default + fi + fi + + echo "Restarting instance....." + systemctl restart $INSTANCE +} + diff --git a/udev_rules.sh b/udev_rules.sh deleted file mode 100755 index 1b186cf..0000000 --- a/udev_rules.sh +++ /dev/null @@ -1,217 +0,0 @@ -#!/bin/bash - -if (( $EUID != 0 )); then - echo "Please run as root (sudo)" - exit -fi - - -# from stackoverflow.com/questions/3231804 -prompt_confirm() { - while true; do - read -r -n 1 -p "${1:-Continue?} [y/n]: " REPLY - case $REPLY in - [yY]) echo ; return 0 ;; - [nN]) echo ; return 1 ;; - *) printf " \033[31m %s \n\033[0m" "invalid input" - esac - done -} -# from unix.stackexchange.com/questions/391293 -log () { - if [ -z "$1" ]; then - cat - else - printf '%s\n' "$@" - fi | tee -a "$logfile" -} - -detect_printer() { - echo - echo - dmesg -C - echo "Plug your printer in via USB now (detection time-out in 1 min)" - counter=0 - while [[ -z "$UDEV" ]] && [[ $counter -lt 60 ]]; do - TEMPUSB=$(dmesg | sed -n -e 's/^.*\(cdc_acm\|ftdi_sio\|ch341\|cp210x\) \([0-9].*[0-9]\): \(tty.*\|FTD.*\|ch341-uart.*\|cp210x\).*/\2/p') - UDEV=$(dmesg | sed -n -e 's/^.*SerialNumber: //p') - counter=$(( $counter + 1 )) - if [[ -n "$TEMPUSB" ]] && [[ -z "$UDEV" ]]; then - break - fi - sleep 1 - done - dmesg -C -} - -detect_camera() { - dmesg -C - echo "Plug your camera in via USB now (detection time-out in 1 min)" - counter=0 - while [[ -z "$CAM" ]] && [[ $counter -lt 60 ]]; do - CAM=$(dmesg | sed -n -e 's/^.*SerialNumber: //p') - TEMPUSBCAM=$(dmesg | sed -n -e 's|^.*input:.*/\(.*\)/input/input.*|\1|p') - counter=$(( $counter + 1 )) - if [[ -n "$TEMPUSBCAM" ]] && [[ -z "$CAM" ]]; then - break - fi - sleep 1 - done - dmesg -C -} - -# initiate logging -logfile='udev.log' -echo "$(date) starting udev rules" >> $logfile - -do_printer() { - echo "UNPLUG PRINTER YOU ARE INSTALLING NOW (other printers can remain)" - echo "Enter the name for new printer/instance (no spaces):" - read INSTANCE - if [ -z "$INSTANCE" ]; then - echo "No instance given. Exiting" | log - exit 1 - fi - - - if prompt_confirm "Begin auto-detect printer serial number for udev entry?" - then - echo - detect_printer - else - echo "OK. Restart when you are ready" | log; exit 0 - fi - - - if [ -z "$UDEV" ]; then - echo "Printer Serial Number not detected" - prompt_confirm "Do you want to use the physical USB port to assign the udev entry? If you use this any USB hubs and printers detected this way must stay plugged into the same USB positions on your machine as they are right now" || exit 0 - echo - USB=$TEMPUSB - echo "Your printer will be setup at the following usb address:" - echo $USB | log - echo - else - echo "Serial number detected as: $UDEV" | log - fi - - echo - - - if prompt_confirm "Ready to write udev entry. Do you want to proceed?" - then - #Printer udev identifier technique - either Serial number or USB port - #Serial Number - if [ -n "$UDEV" ]; then - #echo $UDEV - echo SUBSYSTEM==\"tty\", ATTRS{serial}==\"$UDEV\", SYMLINK+=\"octo_$INSTANCE\" >> /etc/udev/rules.d/99-octoprint.rules - echo This printer can now always be found at /dev/octo_$INSTANCE - fi - - #USB port - if [ -n "$USB" ]; then - #echo $USB - echo KERNELS==\"$USB\",SUBSYSTEM==\"tty\",SYMLINK+=\"octo_$INSTANCE\" >> /etc/udev/rules.d/99-octoprint.rules - echo This printer can now always be found at /dev/octo_$INSTANCE - fi - - #Reset udev - udevadm control --reload-rules - udevadm trigger - - - fi - main_menu -} - -do_camera() { - echo "Unplug the camera you are going to install if is already plugged in" - echo "Enter the name for new camera (no spaces):" - read INSTANCE - if [ -z "$INSTANCE" ]; then - echo "No instance given. Exiting" | log - exit 1 - fi - - - if prompt_confirm "Begin auto-detect printer serial number for udev entry?" - then - echo - detect_camera - else - echo "OK. Restart when you are ready" | log; exit 0 - fi - - #Failed state. Nothing detected - if [ -z "$CAM" ] && [ -z "$TEMPUSBCAM" ] ; then - echo - echo -e "\033[0;31mNo camera was detected during the detection period.\033[0m" - echo - return - fi - - if [ -z "$CAM" ]; then - echo "Camera Serial Number not detected" | log - echo -e "Camera will be setup with physical USB address of \033[0;34m $TEMPUSBCAM.\033[0m" | log - echo "The camera will have to stay plugged into this location." | log - USBCAM=$TEMPUSBCAM - else - echo -e "Camera detected with serial number: \033[0;34m $CAM \033[0m" | log - check_sn "$CAM" - fi - - #Serial Number - if [ -n "$CAM" ]; then - echo SUBSYSTEM==\"video4linux\", ATTRS{serial}==\"$CAM\", ATTR{index}==\"0\", SYMLINK+=\"cam_$INSTANCE\" >> /etc/udev/rules.d/99-octoprint.rules - fi - - #USB port camera - if [ -n "$USBCAM" ]; then - echo SUBSYSTEM==\"video4linux\",KERNELS==\"$USBCAM\", SUBSYSTEMS==\"usb\", ATTR{index}==\"0\", DRIVERS==\"uvcvideo\", SYMLINK+=\"cam_$INSTANCE\" >> /etc/udev/rules.d/99-octoprint.rules - fi - - echo "Camera rule written. The camera will now be found at /dev/cam_$INSTANCE" - #Reset udev - udevadm control --reload-rules - udevadm trigger - main_menu -} - -main_menu() { - VERSION=0.0.1 - #reset - UDEV='' - TEMPUSB='' - CAM='' - TEMPUSBCAM='' - INSTANCE='' - echo - echo - echo "*************************" - echo "udev_rules $VERSION" - echo "*************************" - echo - PS3='Select udev rule to create: ' - - options=("Printer rule" "USB camera rule" "Quit") - - - select opt in "${options[@]}" - do - case $opt in - "Printer rule") - do_printer - break ;; - "USB camera rule") - do_camera - break ;; - "Quit") - exit 1 - ;; - *) echo "invalid option $REPLY";; - esac - - done -} - -main_menu \ No newline at end of file diff --git a/util.sh b/util.sh new file mode 100644 index 0000000..59e4c14 --- /dev/null +++ b/util.sh @@ -0,0 +1,402 @@ +#!/bin/bash + +# from stackoverflow.com/questions/3231804 +prompt_confirm() { + while true; do + read -r -n 1 -p "${green}${1:-Continue?}${white} ${yellow}[y/n]${white}: " REPLY + case $REPLY in + [yY]) echo ; return 0 ;; + [nN]) echo ; return 1 ;; + *) printf " \033[31m %s \n\033[0m" "invalid input" + esac + done +} +# from unix.stackexchange.com/questions/391293 +log () { + if [ -z "$1" ]; then + cat + else + printf '%s\n' "$@" + fi | tee -a "$logfile" +} + +#https://gist.github.com/wellsie/56a468a1d53527fec827 +has-space () { + [[ "$1" != "${1%[[:space:]]*}" ]] && return 0 || return 1 +} + +get_settings() { + #Get octoprint_deploy settings, all of which are written on system prepare + if [ -f /etc/octoprint_deploy ]; then + OCTOEXEC=$(cat /etc/octoprint_deploy | sed -n -e 's/^octoexec: \(\.*\)/\1/p') + OCTOPIP=$(cat /etc/octoprint_deploy | sed -n -e 's/^octopip: \(\.*\)/\1/p') + STREAMER=$(cat /etc/octoprint_deploy | sed -n -e 's/^streamer: \(\.*\)/\1/p') + HAPROXY=$(cat /etc/octoprint_deploy | sed -n -e 's/^haproxy: \(\.*\)/\1/p') + fi +} + +global_config() { + echo "This utility allows you to modify OctoPrint settings for indivdual or all instances." + echo "There is no error checking so it is critical to set the input parameters correctly." + echo "See the Wiki for more details." + echo "Enter the config and parameter" + read -p $GC + get_instances true + select opt in "${INSTANCE_ARR[@]}" + do + if [ "$opt" == Quit ]; then + main_menu + fi + echo "Sorry this doesn't do anything yet" + done + +} + +octo_deploy_update() { + sudo -u $user git -C $SCRIPTDIR pull + exit +} + +back_up() { + INSTANCE=$1 + echo "Creating backup of $INSTANCE...." + d=$(date '+%Y-%m-%d') + sudo -p $user tar -czf ${INSTANCE}_${d}_backup.tar.gz -C /home/$user/ .${INSTANCE} + echo "Tarred and gzipped backup created in /home/$user" +} + +restore() { + INSTANCE=$1 + TAR=$2 + echo "Restoring backup of $INSTANCE...." + systemctl stop $INSTANCE + sudo -p $user tar -xvf $TAR + systemctl start $INSTANCE + +} + +back_up_all() { + get_settings + get_instances false + for instance in "${instances[@]}"; do + echo $instance + back_up $instance + done + +} + +get_instances() { + addquit=$1 + readarray -t INSTANCE_ARR < <(cat /etc/octoprint_instances | sed -n -e 's/^instance:\([[:graph:]]*\) .*/\1/p') + if [ "$addquit" == true ]; then + INSTANCE_ARR+=("Quit") + fi +} + +get_cameras() { + addquit=$1 + readarray -t CAMERA_ARR < <(cat /etc/octoprint_cameras | sed -n -e 's/^camera:\([[:graph:]]*\) .*/\1/p') + if [ "$addquit" == true ]; then + CAMERA_ARR+=("Quit") + fi +} + +sync_users() { + echo + echo + echo "This will sync all the users from one instance to all the other instances." + PS3="${green}Select instance that contains current user list: ${white}" + get_instances true + select opt in "${INSTANCE_ARR[@]}" + do + if [ "$opt" == Quit ]; then + main_menu + fi + + if prompt_confirm "Copy users from instance $opt to all other instances?"; then + userfile=/home/$user/.$opt/users.yaml + #re-read to avoid the Quit + get_instances false + for instance in "${INSTANCE_ARR[@]}"; do + if [ "$instance" == "$opt" ]; then + continue + fi + sudo -u $user cp $userfile /home/$user/.$instance/ + done + + if prompt_confirm "Restart all instances now for changes to take effect?"; then + restart_all + fi + fi + + main_menu + done +} + +share_uploads() { + get_settings + echo + echo + echo "This option will make all your uploads go to a single instance." + echo "This will mean all gcode files be available for all your instances." + echo "Use this option only if you understand the implications." + echo "This can be adjusted later in the Folders settings of OctoPrint." + PS3="${green}Select instance where uploads will be stored: ${white}" + get_instances false + INSTANCE_ARR+=("Custom" "Quit") + select opt in "${INSTANCE_ARR[@]}" + do + if [ "$opt" == Quit ]; then + main_menu + break + fi + + if [ "$opt" == "Custom" ]; then + echo "Enter full path (should start /home/$user/):" + read ULPATH + if [ -d "$ULPATH" ]; then + if prompt_confirm "This folder already exists. Are you sure you want to use it?"; then + opt=$ULPATH + else + echo "Restart the option if you change your mind" + main_menu + break + fi + else + sudo -u $user mkdir $ULPATH + opt=$ULPATH + fi + + else + opt=/home/$user/.$opt/uploads + fi + echo + #Remove Quit and Custom from array, is there a cleaner way? + unset 'options[-1]' + unset 'options[-1]' + for instance in "${options[@]}"; do + $OCTOEXEC --basedir /home/$user/.$instance config set folder.uploads "$opt" + done + break + done + echo "${cyan}Instances must be restarted for changes to take effect.${white}" + main_menu +} + +add_udev() { + #get instances that don't have a udev rule + PS3="${green}Select instance to add udev rule: ${white}" + readarray -t noudev < <(fgrep "udev:false" /etc/octoprint_instances 2> /dev/null | sed -n -e 's/^instance:\([[:graph:]]*\) .*/\1/p') + noudev+=("Quit") + select opt in "${noudev[@]}" + do + if [ "$opt" == Quit ]; then + main_menu + fi + INSTANCE=$opt + detect_printer + printer_udev false + printer_udev true + #this needs more thought + sed -i "s/^\(instance:$INSTANCE port:.* udev:\)false/\1true/" /etc/octoprint_instances + break + done + udevadm control --reload-rules + udevadm trigger + echo "${cyan}udev rule has been added${white}" + main_menu +} + +remove_udev() { + PS3="${green}Select udev rule to remove: ${white}" + readarray -t udevs < <(fgrep "udev:true" /etc/octoprint_instances 2> /dev/null | sed -n -e 's/^instance:\([[:graph:]]*\) .*/\1/p') + udevs+=("Quit") + select opt in "${udevs[@]}" + do + if [ "$opt" == Quit ]; then + main_menu + fi + sed -i "/$opt/d" /etc/udev/rules.d/99-octoprint.rules + sed -i "s/^\(instance:$opt port:.* udev:\)true/\1false/" /etc/octoprint_instances + break + done + echo "${cyan}udev rule has been removed${white}" + udevadm control --reload-rules + udevadm trigger + main_menu +} + +add_udev_camera() { + PS3="${green}Select camera to add udev rule: ${white}" + readarray -t noudev < <(fgrep "udev:false" /etc/octoprint_cameras 2> /dev/null | sed -n -e 's/^camera:\([[:graph:]]*\) .*/\1/p') + noudev+=("Quit") + select opt in "${noudev[@]}" + do + if [ "$opt" == Quit ]; then + main_menu + fi + INSTANCE=$opt + detect_camera + write_cam_udev + sed -i "s/^\(camera:$opt port:.* udev:\)false/\1true/" /etc/octoprint_instances + break + done + udevadm control --reload-rules + udevadm trigger + echo "${cyan}Camera udev rule has been added${white}" + main_menu +} + +remove_udev_camera() { + PS3="${green}Select udev rule to remove: ${white}" + readarray -t udevs < <(fgrep "udev:true" /etc/octoprint_cameras 2> /dev/null | sed -n -e 's/^camera:\([[:graph:]]*\) .*/\1/p') + udevs+=("Quit") + select opt in "${udevs[@]}" + do + if [ "$opt" == Quit ]; then + main_menu + fi + sed -i "/$opt/d" /etc/udev/rules.d/99-octoprint.rules + sed -i "s/^\(camera:$opt port:.* udev:\)true/\1false/" /etc/octoprint_cameras + break + done + echo "${cyan}Camera udev rule has been removed${white}" + udevadm control --reload-rules + udevadm trigger + main_menu +} + +instance_status() { + clear + echo + echo "${cyan}*******************************************${white}" + get_instances false + readarray -t cameras < <(ls -1 /etc/systemd/system/cam*.service 2> /dev/null | sed -n -e 's/^.*\/\(.*\).service/\1/p') + #combine instances and cameras + INSTANCE_ARR+=(${cameras[@]}) + echo "Service - Status:" + echo "------------------" + for instance in "${INSTANCE_ARR[@]}"; do + status=$(systemctl status $instance | sed -n -e 's/Active: \([[:graph:]]*\) .*/\1/p') + if [ $status = "active" ]; then + status="${green}$status${white}" + elif [ $status = "failed" ]; then + status="${red}$status${white}" + fi + echo "$instance - $status" + done + echo "${cyan}*******************************************${white}" + echo "Only instances and cameras made with octoprint_deploy are shown" + echo + main_menu +} + +change_streamer() { + echo + echo "${cyan}This allows you to change the default webcam streamer." + echo "Please note, it DOES NOT change the streamer for existing cameras." + echo "You will need to delete and reinstall those cameras for changes to take effect.${white}" + echo + streamer_install +} + +remove_everything() { + get_settings + if prompt_confirm "Remove everything?"; then + get_instances false + unset 'instances[0]' + readarray -t cameras < <(ls -1 /etc/systemd/system/cam*.service 2>/dev/null | sed -n -e 's/^.*\/\(.*\).service/\1/p') + for instance in "${INSTANCE_ARR[@]}"; do + remove_instance $instance + done + + for camera in "${cameras[@]}"; do + remove_camera $camera + done + + echo "Removing system stuff" + rm /etc/systemd/system/octoprint.service + rm /etc/octoprint_streamer + rm /etc/octoprint_deploy + rm /etc/octoprint_instances + rm /etc/octoprint_cameras + rm /etc/udev/rules.d/99-octoprint.rules + rm /etc/sudoers.d/octoprint_reboot + rm /etc/sudoers.d/octoprint_systemctl + rm -rf /home/$user/.octoprint + rm -rf /home/$user/OctoPrint + rm -rf /home/$user/ustreamer + rm -rf /home/$user/mjpg-streamer + systemctl restart haproxy.service + systemctl daemon-reload + + fi +} + +restart_all() { + get_settings + get_instances false + for instance in "${INSTANCE_ARR[@]}"; do + echo "Trying to restart instance $instance" + systemctl restart $instance + done + main_menu +} + +check_sn() { + if [ -f "/etc/udev/rules.d/99-octoprint.rules" ]; then + if grep -q $1 /etc/udev/rules.d/99-octoprint.rules; then + echo "${red}An identical serial number has been detected in the udev rules. Please be warned, this will likely cause instability!${white}" + else + echo "${cyan}No duplicate serial number detected.${white}" + fi + fi +} + +usb_testing() { + echo + echo + echo "Testing printer USB" + detect_printer + echo "Detected device at $TEMPUSB" + echo "Serial Number detected: $UDEV" + main_menu +} + +diagnostic_output() { + echo "**************************************" + echo "$1" + echo "**************************************" + cat $1 + +} + +diagnostics() { + get_settings + logfile='octoprint_deploy_diagnostic.log' + echo "octoprint_deploy diagnostic information. Please provide ALL output for support help" + diagnostic_output /etc/octoprint_deploy | log + diagnostic_output /etc/octoprint_instances | log + diagnostic_output /etc/octoprint_cameras | log + diagnostic_output /etc/udev/rules.d/99-octoprint.rules | log + #get all instance status + get_instances false + for instance in "${INSTANCE_ARR[@]}"; do + echo "**************************************" | log + systemctl status $instance -l --no-pager | log + #get needed config info + sudo -u $user $OCTOEXEC --basedir=/home/$user/.$INSTANCE config get plugins.classicwebcam | log + sudo -u $user $OCTOEXEC --basedir=/home/$user/.$INSTANCE config get webcam | log + done + #get all cam status + get_cameras false + for camera in "${CAMERA_ARR[@]}"; do + echo "**************************************" | log + systemctl status $camera -l --no-pager | log + done + #get haproxy status + echo "**************************************" | log + systemctl status haproxy -l --no-pager | log + logfile='octoprint_deploy.log' + main_menu +} \ No newline at end of file