Launching a bash script on boot on OS X - macos

UPDATE:
The proposed link was the solution to this question. On a Mac you have to make symlink:
sudo ln -s /usr/local/bin/node /usr/bin/node
I wanted to automate some processes on boot of a mac mini.
The script should fire up some node.js scripts (forever). The script itself is fired by a launchd process. I have several scripts that work like that. But that one I have a problem:
#!/bin/bash
export PATH=/usr/local/bin
export NODE_ENV=production
cd /Volumes/Services/Proxy
forever -a start /Volumes/Services/Proxy/app.js
This script works pretty fine if executed in the terminal. It makes what it does.
But this script throws errors on std.err when launched on boot.
env: node: no such file or directory
First I thought it's a problem because the PATH to "forever" ist not known on startup so I added the export PATH explicitly.
It throws the same error when I give the absolute path to "forever":
#!/bin/bash
export NODE_ENV=production
cd /Volumes/Services/Proxy
/usr/local/bin/forever -a start /Volumes/Services/Proxy/app.js
There is no error in the launched.plist file. If I add other content to this script like for example mkdir FOO it does this without problems.
... im not so firm with shell scripting... :(
Anyone knows what I'm doin wrong?

Related

How to launch WSL as if I've logged in?

I have a WSL Ubuntu distro that I've set up so that when I login 4 services start working, including a web API that I can test via Swagger to verify it is up and working.
I'm at the point where what I want to do now is start WSL via a script - that is, launch my distro, have all of the services start, and do it from Python. The problem is I cannot even figure out the correct syntax to get WSL to start from PowerShell in a manner where my services start.
Side note: "services" != systemctl (or similar) calls, but just executing bash CLI commands from either my .bashrc or .profile at login.
I've put the commands to execute in .profile & .bashrc. I've configured it both for root execution and non-root user execution. I've taken the commands out of those 2 files and put it into a script in the Windows file system that I pass in on the start of wsl. And I've put that shell script in the WSL file system as well. Nothing seems to work, and sometimes the distro starts and then stops after about 30 seconds.
Some of the PS CLI commands I've tried:
Start-Job -ScriptBlock{ wsl -d distro -u root }
Start-Job -ScriptBlock{ wsl -d distro -u root 'bash -i -l -c /root/bin/start.sh'
Start-Job -ScriptBlock{ wsl -d distro -u root 'bash -i -l -c .\start.sh'
wsl -d distro -u root -- bash -i -l -c /root/bin/start.sh
wsl -d distro -u root -- bash -i -l -c .\start.sh
wsl -d distro -u root -- /root/bin/start.sh
Permutations of the above that I've tried: replace root with my default login, and turning all of the Start-Job bash options into a comma-separated list of single-quoted strings (Ex: 'bash', '-i', '-l', ... ). Nothing I launch from the CLI will allow me access to the web API that is supposed to be hosted on my distro.
Any advice on what to try next?
Not necessarily an answer here as much as troubleshooting tips which will hopefully lead to an answer:
First, most of the forms that you are using seem to be correct. The only ones that absolutely shouldn't work are those that attempt to run the script from the Windows filesystem.
Make sure that you have a shebang line starting your script. I'm assuming you do, but other readers may come across this as well. For the moment, try this form:
#!/usr/bin/env -S bash -li
That's going to have the same effect as the bash -li you tried -- It will source both both interactive startup files such as ~/.bashrc as well as login profiles such as ~/.bash_profile (and /etc/profile.d/*, etc.).
Note that preferably, you won't need the -li. Best practice would be to move anything necessary for the services over from the startup scripts to your start.sh script, and avoid parsing the profile and rc. I need to go update some of my answers, since I just realized I've been guilty of giving some potentially bad advice ...
Specifically, though, I'm wondering if your interactive Bash config has something truly, well, "interactive" in it that might be preventing the automatic running of the script itself. Again, best practice would be for ~/.bashrc to only hold configuration that is needed for interactive shell sessions.
Make sure the script is set as executable (chmod +x start.sh). Again, I'm assuming this is the case for you.
With a shebang line and an executable script, use something like:
wsl -d distro -u root -e /root/bin/start.sh
The -e tells WSL to launch the script directly. Since it has a shebang line, it will be parsed by Bash. Most of the other forms you use above actually run Bash twice - Once when launching WSL and another when it finds the shebang line in the script.
Try some basic troubleshooting for your script like:
Add set -x to the top (right under the shebang line) to turn on script debugging.
Add a ps -efH at the end to show the processes that are running when the script completes
If needed, resort to quick-and-dirty echo statements to show where things have progressed in the script.
I'm hopeful that the above will at least show you the problem, but if not, add the debugging info that you gain from this to your question, and we can troubleshoot further.

PM2 startup on macOS Catalina

I'm trying to set pm2 to auto start on Mac OS like this pm2 startup
However I keep getting this error:
env: Fusion.app/Contents/Public:/usr/local/bin: No such file or directory
I tried the suggestion from this page: How to use pm2 startup command on Mac? But the even using this command I keep getting the same error: pm2 startup darwin
How can I get pm2 to startup automatically on Mac OS Catalina?
Thanks!
When you run pm2 startup command it suggests you PATH variable. It seems that this variable contains spaces that causes this error. Try to get rid of Fusion.app part of it.
Expanding on zored's answer, what's probably happening is that pm2 isn't generating the correct script because your PATH variable contains spaces (most likely "VMWare Fusion" in your case).
Run echo $PATH, clean it up by escaping the space character using a backslash (\), then manually paste the resulting PATH variable in pm2's generated startup script.
So basically instead of running a generated pm2 script like this:
sudo env PATH=$PATH:/usr/local/bin /usr/local/lib/node_modules/pm2/bin/pm2 startup launchd -u <user> --hp <user_folder>
run something like this:
sudo env PATH=<your `echo $PATH` output with escaped spaces>:/usr/local/bin /usr/local/lib/node_modules/pm2/bin/pm2 startup launchd -u <user> --hp <user_folder>
In case you wonder where the VMWare path parameter is set (and could be changed permanently):
/etc/paths.d/com.vmware.fusion.public
The content of this file is added to the PATH environment

How to launch a new process in a new terminal in Linux

I'm using Linux Mint 19 Cinnamon. I've an Angular project and I want to do the following things. All these in a separate terminal:
Navigate into project folder and run code . so that I can see the code on Visual studio.
Navigate into project folder and run ng serve to launch my application and keep it running.
Navigate into project folder and run node server.js to start backend server and keep it running.
this is very painful as the project location is-
home/xpert/Documents/social-coder
and it contains two servers:
social-coder/application The angular server that runs on localhost:4200
social-coder/server/server.js The backend server that runs on localhost:3000
Both the servers need to be constantly running for the application to work.
Navigating again and again with a new terminal is what wasting my time. I decided to write a shell script so that I can do all at once with one single click. This is what my .bashrc file contains:
alias first='gnome-terminal | npm start --prefix Documents/social-coder/application'
alias second='node Documents/social-coder/server/server.js'
alias third='code Documents/social-coder'
This is what I've thought. SO, all of the above 3 commands works perfectly If I manually copy-paste them in separate terminals. But again, I have to manually open 3 different terminals and make them run. I'm coming from:
How to write practical shell scripts - Like Geeks
Run command on another(new) terminal window
How to Use GNOME Terminal App
gnome-terminal opens a new terminal but still runs npm start from the same terminal itself.
I admit that I'm a newbie and there are gaps in my knowledge. Please correct me.
For now I've created a command using pipe. It is working but I'm not sure whether this is the preferred way or not:
gnome-terminal -- /bin/bash -c 'npm start --prefix Documents/social-coder/application' | gnome-terminal -- /bin/bash -c 'node Documents/social-coder/server/server.js' | gnome-terminal -- /bin/bash -c 'code Documents/social-coder'
Please feel free to edit this answer.

Run an shell script on startup (not login) on Ubuntu 14.04

I have a build server. I'm using the Azure Build Agent script. It's a shell script that will run continuously while the server is up. Problem is that I cannot seem to get it to run on startup. I've tried /etc/init.d and /etc/rc.local and the agent is not being run. Nothing concerning the build agent in the boot logs.
For /etc/init.d I created the script agent.sh which contains:
#!/bin/bash
sh ~/agent/run.sh
Gave it the proper permissions chmod 755 agent.shand moved it to /etc/init.d.
and for /etc/rc.local, I just appended the following
sh ~/agent/run.sh &
before exit 0.
What am I doing wrong?
EDIT: added examples.
EDIT 2: Just noticed that the init.d README says that shell scripts need to start with #!/bin/sh and not #!/bin/bash. Also used absolute path, but no change.
FINAL EDIT: As #ewrammer suggested, I used cron and it worked. crontab -e and then #reboot /home/user/agent/run.sh.
It is hard to see what is wrong if you are not posting what you have done, but why not add it as a cron job with #reboot as pattern? Then cron will run the script every time the computer starts.
Just in case, using a supervisor could be a good idea, In Ubuntu 14 you don't have systemd but you can choose from others https://en.wikipedia.org/wiki/Process_supervision.
If using immortal, after installing it, you just need to create a run.yml file in /etc/immortal with something like:
cmd: /path/to/command
log:
file: /var/log/command.log
This will start your script/command on every start, besides ensuring your script/app is always up and running.

Phonegap CLI runs from SSH session but not from bash script

I am in the process of setting up a Ubuntu 14.04 server to automate hybrid Android app builds with Phonegap CLI. Having written up all the relevant scripts I ran into a rather strange problem - when I SSH in to my server I can run the script and run all Phonegap commands successfully in my interactive shell session. However, every attempt to run those same commands in an automated script that gets triggered by some other - visitor generated - event fails. To pin down the problem I reduced it down to a simple experiment which I outline below.
Step 1 - write a startup script, pgtest in /etc/init.d
#! /bin/bash
source ~/.nvm/nvm.sh;
nvm use stable;
cd /home;
ls >> /tmp/ls;
which node >> /tmp/node;
which git >> /tmp/git;
which phonegap >> /tmp/pgp;
phonegap -v >> /tmp/pgpv 2>/tmp/pgpe;
Explanations
I use NVM to manage Node so I am making sure that the system knows where to find nvm.sh
I am firing up NVM to use the stable (4.1.1.) version of Node + NPM
I want to make sure that my batch file is actually being executed so I do ls /home and pipe its output to the /tmp/ls file.
I want to be sure that node, git and phonegap are actually available so I do pipe the output from which node|git|phonegap to files in the /tmp folder.
Little point in complicating things so I am issuing the simplest of Phonegap commands, phonegap -v to report the current version number. Any errors that might happen when doing this are being piped into the file /tmp/pgpe.
Step 2 - Make sure that pgtest is run last
ln -s /etc/init.d/pgtest /etc/rc2.d/S04PGTest
Explanation - I only want this script to be run after everything else on my server has had a chance to startup
With all of this in place I rebooted by server and examined the contents of the /tmp folder. My findings
ls - the folder listing for the /home folder present and correct.
node, git and pgp point to the locations of Node, Git and Phonegap
pgpv, which should contain the Phonegap version number, is EMPTY
pgpe is present and NOT empty
That last implies that the system encountered an error whilst attempting to execute phonegap -v. Here are the contents of pgpe.
path.js:8
throw new TypeError('Path must be a string. Received ' +
^
TypeError: Path must be a string. Received undefined
at assertPath (path.js:8:11)
at Object.posix.join (path.js:477:5)
at Object.
(/root/.nvm/versions/node/v4.1.1/lib/node_modules/phonegap/node_modules/phonegap-build/lib/common/config/global.js:17:28)
at Module._compile (module.js:434:26)
at Object.Module._extensions..js (module.js:452:10)
at Module.load (module.js:355:32)
at Function.Module._load (module.js:310:12)
at Module.require (module.js:365:17)
at require (module.js:384:17)
at Object.
(/root/.nvm/versions/node/v4.1.1/lib/node_modules/phonegap/node_modules/phonegap-build/lib/common/config.js:9:13)
Now here is the curious thing. If I clear out the /tmp folder and issue a /etc/init.d/pgtest in an interactive shell session I get the following results
/tmp/ls present and populated with the /home folder listing as before
/tmp/node, /tmp/git /tmp/pgp present and correct
/tmp/pgpvreports 5.3.6 - the current Phonegap version number
/tmp/pgpe is EMPTY , i.e, no errors are reported
Clearly, the interactive bash shell environment has something that is not present when I run an automated script - at startup in this case but it also happens when I trigger the process via an automated script in any other way.
With all of this I am moving closer to pinning down the cause of the problem. However, there my knowledge of how these systems work is letting me down. What is the difference between the interactive shell environment and the one that is encountered by my automated script? Just how do I interpret the errors reported in /tmp/pgpe? What do I do to fix them?
I'd be most grateful to anyone who might be able to put me on the right track here.
Edit in light of #Eduardo's suggestions. I grabbed the two sets of environments (interactive & init.d). The results of doing a DIFF (interactive vs init.d) can be found in this fiddle. A somewhat less accessible dump of the DIFF result is shown below
--- /home/env.inter 2015-11-11 08:30:40.314172560 +0000
+++ /home/env.auto 2015-11-11 08:32:55.240906000 +0000
## -1,48 +1,38 ##
BASH=/bin/bash
BASHOPTS=cmdhist:complete_fullquote:extquote:force_fignore:hostcomplete:interactive_comments:login_shell:progcomp:promptvars:sourcepath
BASH_ALIASES=()
-BASH_ARGC=()
-BASH_ARGV=()
+BASH_ARGC=([0]="1")
+BASH_ARGV=([0]="start")
BASH_CMDS=()
BASH_LINENO=([0]="0")
-BASH_SOURCE=([0]="/etc/init.d/pgtest")
+BASH_SOURCE=([0]="/etc/rc2.d/S04pgtest")
BASH_VERSINFO=([0]="4" 1="3" [2]="11" [3]="1" [4]="release" [5]="x86_64-pc-linux-gnu")
BASH_VERSION='4.3.11(1)-release'
DIRSTACK=()
EUID=0
GROUPS=()
-HOME=/root
HOSTNAME=example.com
HOSTTYPE=x86_64
IFS=$' \t\n'
-LANG=en_US.UTF-8
-LESSCLOSE='/usr/bin/lesspipe %s %s'
-LESSOPEN='| /usr/bin/lesspipe %s'
-LOGNAME=root
-LS_COLORS='rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:.tar=01;31:.tgz=01;31:.arj=01;31:.taz=01;31:.lzh=01;31:.lzma=01;31:.tlz=01;31:.txz=01;31:.zip=01;31:.z=01;31:.Z=01;31:.dz=01;31:.gz=01;31:.lz=01;31:.xz=01;31:.bz2=01;31:.bz=01;31:.tbz=01;31:.tbz2=01;31:.tz=01;31:.deb=01;31:.rpm=01;31:.jar=01;31:.war=01;31:.ear=01;31:.sar=01;31:.rar=01;31:.ace=01;31:.zoo=01;31:.cpio=01;31:.7z=01;31:.rz=01;31:.jpg=01;35:.jpeg=01;35:.gif=01;35:.bmp=01;35:.pbm=01;35:.pgm=01;35:.ppm=01;35:.tga=01;35:.xbm=01;35:.xpm=01;35:.tif=01;35:.tiff=01;35:.png=01;35:.svg=01;35:.svgz=01;35:.mng=01;35:.pcx=01;35:.mov=01;35:.mpg=01;35:.mpeg=01;35:.m2v=01;35:.mkv=01;35:.webm=01;35:.ogm=01;35:.mp4=01;35:.m4v=01;35:.mp4v=01;35:.vob=01;35:.qt=01;35:.nuv=01;35:.wmv=01;35:.asf=01;35:.rm=01;35:.rmvb=01;35:.flc=01;35:.avi=01;35:.fli=01;35:.flv=01;35:.gl=01;35:.dl=01;35:.xcf=01;35:.xwd=01;35:.yuv=01;35:.cgm=01;35:.emf=01;35:.axv=01;35:.anx=01;35:.ogv=01;35:.ogx=01;35:.aac=00;36:.au=00;36:.flac=00;36:.mid=00;36:.midi=00;36:.mka=00;36:.mp3=00;36:.mpc=00;36:.ogg=00;36:.ra=00;36:.wav=00;36:.axa=00;36:.oga=00;36:.spx=00;36:.xspf=00;36:'
MACHTYPE=x86_64-pc-linux-gnu
-MAIL=/var/mail/root
-NVM_DIR=/root/.nvm
-NVM_IOJS_ORG_MIRROR=https://iojs.org/dist
-NVM_NODEJS_ORG_MIRROR=https://nodejs.org/dist
-NVM_RC_VERSION=
OPTERR=1
OPTIND=1
OSTYPE=linux-gnu
-PATH=/opt/android/platform-tools:/opt/android/tools:/opt/android:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
+PATH=/sbin:/usr/sbin:/bin:/usr/bin
PIPESTATUS=([0]="0")
-PPID=4801
+PPID=911
+PREVLEVEL=N
PS4='+ '
-PWD=/etc/init.d
+PWD=/
+RUNLEVEL=2
SHELL=/bin/bash
SHELLOPTS=braceexpand:hashall:interactive-comments
-SHLVL=3
-SSH_CLIENT='nn.nn.nn.nn nnnn nnnn'
-SSH_CONNECTION='nn.nn.nn.nn nnnn nn.nn.nn.nn nnnn'
-SSH_TTY=/dev/pts/0
-TERM=xterm
+SHLVL=1
+TERM=linux
UID=0
-USER=root
-XDG_RUNTIME_DIR=/run/user/1000
-XDG_SESSION_ID=5
+UPSTART_EVENTS=runlevel
+UPSTART_INSTANCE=
+UPSTART_JOB=rc
_=n
+previous=N
+runlevel=2
The only things I have changed here - masked the Host name and the SSH client IP address.
I am pretty sure I had tried this in my own experiments prior to posting this question but following #Eduardo's suggestion below I tried sticking in a
EXPORT PATH=/opt/android/platform-tools:/opt/android/tools:/opt/android:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
at the top of the script - just below the source ~/.nvm... line. A reboot later the result was still the same: an empty /tmp/pgpv and the same errors reported in /tmp/pgpe.
Just make sure to set the PATH variable inside your script to the same, longer one, you see on the diff, then retry automated.
I would probably also define the HOME and NVM* variables. As a test I'd create a test.sh script at the same directory of phonegap, with this content:
#!/bin/bash
set > /tmp/env_vars.log
And have your script as:
#!/bin/bash
export TERM=linux
export USER=root
export HOME=/root
export NVM_DIR=/root/.nvm
export NVM_IOJS_ORG_MIRROR=https://iojs.org/dist
export NVM_NODEJS_ORG_MIRROR=https://nodejs.org/dist
export NVM_RC_VERSION=
export PATH=/opt/android/platform-tools:/opt/android/tools:/opt/android:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
source ~/.nvm/nvm.sh
nvm use stable
cd /home
ls > /tmp/ls
which node > /tmp/node
which git > /tmp/git
which phonegap > /tmp/pgp
phonegap -v > /tmp/pgpv 2>/tmp/pgpe;
test.sh

Resources