Any Monit like equivalents for windows OS? [closed] - system-monitoring

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 6 years ago.
Improve this question
I've seen the question asked "can you run Monit on Windows?", and unless you want to use a VM, the answer appears to be no.
So...are there any small footprint monit-like applications actually for Windows OS's? What I'm looking for is not only monitoring (of which there are hundreds of apps), but also the ability to execute a script or restart a service. For example, monitor a web page, and restart Tomcat if that page becomes unresponsive (can't just watch the service, because the service is still running but not responding properly).
This is for a small application, not a large application, so the heavyweight/expensive solutions aren't desired.

I didn't find anything out there that fit my needs, so I learned a little Powershell scripting and rolled a solution that should be useful to others as well. Assuming a Windows platform (otherwise use monit!), Powershell is really powerful and easy.
sample-monitor.ps1 script:
$webClient = new-object System.Net.WebClient
###################################################
# BEGIN USER-EDITABLE VARIABLES
# the URL to ping
$HeartbeatUrl = "http://someplace.com/somepage/"
# the response string to look for that indicates things are working ok
$SuccessResponseString = "Some Text"
# the name of the windows service to restart (the service name, not the display name)
$ServiceName = "Tomcat6"
# the log file used for monitoring output
$LogFile = "c:\temp\heartbeat.log"
# used to indicate that the service has failed since the last time we checked.
$FailureLogFile = "c:\temp\failure.log"
# END USER-EDITABLE VARIABLES
###################################################
# create the log file if it doesn't already exist.
if (!(Test-Path $LogFile)) {
New-Item $LogFile -type file
}
$startTime = get-date
$output = $webClient.DownloadString($HeartbeatUrl)
$endTime = get-date
if ($output -like "*" + $SuccessResponseString + "*") {
# uncomment the below line if you want positive confirmation
#"Success`t`t" + $startTime.DateTime + "`t`t" + ($endTime - $startTime).TotalSeconds + " seconds" >> $LogFile
# remove the FailureLog if it exists to indicate we're in good shape.
if (Test-Path $FailureLogFile) {
Remove-Item $FailureLogFile
}
}
else {
"Fail`t`t" + $startTime.DateTime + "`t`t" + ($endTime - $startTime).TotalSeconds + " seconds" >> $LogFile
# restart the service if this is the first time it's failed since the last successful check.
if (!(Test-Path $FailureLogFile)) {
New-Item $FailureLogFile -type file
"Initial failure:" + $startTime.DateTime >> $FailureLogFile
Restart-Service $ServiceName
}
}
The only logic in this script is that it will only try to restart the service once after an initial failure. This is to prevent a situation where a service takes a while to restart, and while it's restarting, the monitor keeps seeing the failure and restarts again (bad infinite loop). Otherwise you can do just about anything, like add email notifications, or do more than just restart a service.
This script will execute once, which means you'll need to control its repetition externally. You could put it in an infinite loop right in the script, but that seems a little flaky. I used windows Task Scheduler, executing it like so:
Program: Powershell.exe
arguments: -command "C:\projects\foo\scripts\monitor.ps1" -noprofile
Start In: C:\projects\foo\scripts
You could also use a more robust scheduler like VisualCron, plug it into a windows service, or via an application server scheduler like Quart.NET. In my case the task scheduler works fine.

I am using ipsentry from RGE Inc (http://www.ipsentry.com/).
Have been using it for several years, saved me many many times.
No affiliation with them, this is not an advertisement, just info from a satisfied customer.

I adjusted a little the Dan Tanner script when he could not connect, showed an error and did not restart the service
$webClient = new-object System.Net.WebClient
###################################################
# BEGIN USER-EDITABLE VARIABLES
# the URL to ping
$HeartbeatUrl = "http://localhost:8080/"
# the response string to look for that indicates things are working ok
$SuccessResponseString = "Apache"
# the name of the windows service to restart (the service name, not the display name)
$ServiceName = "Tomcat6"
# the log file used for monitoring output
$LogFile = "c:\temp\log.log"
# used to indicate that the service has failed since the last time we checked.
$FailureLogFile = "c:\temp\log2.log"
# END USER-EDITABLE VARIABLES
###################################################
# create the log file if it doesn't already exist.
if (!(Test-Path $LogFile)) {
New-Item $LogFile -type file
}
$startTime = get-date
try {
$output = $webClient.DownloadString($HeartbeatUrl)
$endTime = get-date
if ($output -like "*" + $SuccessResponseString + "*") {
# uncomment the below line if you want positive confirmation
#"Success`t`t" + $startTime.DateTime + "`t`t" + ($endTime - $startTime).TotalSeconds + " seconds" >> $LogFile
# remove the FailureLog if it exists to indicate we're in good shape.
if (Test-Path $FailureLogFile) {
Remove-Item $FailureLogFile
}
}
else {
"Fail`t`t" + $startTime.DateTime + "`t`t" + ($endTime - $startTime).TotalSeconds + " seconds" >> $LogFile
# restart the service if this is the first time it's failed since the last successful check.
if (!(Test-Path $FailureLogFile)) {
New-Item $FailureLogFile -type file
"Initial failure:" + $startTime.DateTime >> $FailureLogFile
Restart-Service $ServiceName
}
}
}catch [Net.WebException] {
New-Item $FailureLogFile -type file
"Initial failure:" + $startTime.DateTime + $_.Exception.ToString() >> $FailureLogFile
Restart-Service $ServiceName
}

This can be at least partially accomplished using the Service Control Manager that ship with Windows. It monitors service applications and can automatically start them at boot, restart them when it crashes, etc. Writing your application as a service is one option, but if you can't write the application as a service then you can try to wrap the process using srvany.exe in the Windows Resource Kit.
More info about writing a service: https://support.microsoft.com/en-us/kb/137890
As for the actual monitoring features, I'm not entirely sure what's available, or the extend of SCM's capabilities.

Related

Compare a file size between 2 servers (Unix & Windows)

I need to write a Windows batch file to resolve the below issue.
I need to make sure that a file is completely transferred from it's origin server (Unix) to the target server (Windows).
I am trying to compare the file size between the two servers, the solution needs to be in a Windows batch file (I am having trouble with connecting to a remote Unix server using SSH in a DOS batch file)
From the fact you can use SSH, I assume you also have an SFTP access.
You can use following PowerShell script using WinSCP .NET assembly.
param (
$sessionUrl = "sftp://user:mypassword;fingerprint=ssh-rsa-xxxxxxxxx...=#example.com/",
$localPath = "C:\path\file.dat",
$remotePath = "/path/file.dat"
)
$localSize = (Get-Item $localPath).Length
Write-Host "$localPath has $localSize bytes"
# Load WinSCP .NET assembly
Add-Type -Path (Join-Path $PSScriptRoot "WinSCPnet.dll")
# Setup session options
$sessionOptions = New-Object WinSCP.SessionOptions
$sessionOptions.ParseUrl($sessionUrl)
$session = New-Object WinSCP.Session
# Connect
$session.Open($sessionOptions)
$remoteSize = $session.GetFileInfo($remotePath).Length
$session.Dispose()
Write-Host "$remotePath has $remoteSize bytes"
# Compare cheksums
if ($localSize -eq $remoteSize)
{
Write-Host "Match"
$result = 0
}
else
{
Write-Host "Does NOT match"
$result = 1
}
exit $result
Or even better, compare file checksums, not just size, if your server support that, see:
Verify checksum of a remote file against a local file over SFTP/FTP protocol
(I'm the author of WinSCP)

Run R/Rook as a web server on startup

I have created a server using Rook in R - http://cran.r-project.org/web/packages/Rook
Code is as follows
#!/usr/bin/Rscript
library(Rook)
s <- Rhttpd$new()
s$add(
name="pingpong",
app=Rook::URLMap$new(
'/ping' = function(env){
req <- Rook::Request$new(env)
res <- Rook::Response$new()
res$write(sprintf('<h1>Pong</h1>',req$to_url("/pong")))
res$finish()
},
'/pong' = function(env){
req <- Rook::Request$new(env)
res <- Rook::Response$new()
res$write(sprintf('<h1>Ping</h1>',req$to_url("/ping")))
res$finish()
},
'/?' = function(env){
req <- Rook::Request$new(env)
res <- Rook::Response$new()
res$redirect(req$to_url('/pong'))
res$finish()
}
)
)
## Not run:
s$start(port=9000)
$ ./Rook.r
Loading required package: tools
Loading required package: methods
Loading required package: brew
starting httpd help server ... done
Server started on host 127.0.0.1 and port 9000 . App urls are:
http://127.0.0.1:9000/custom/pingpong
Server started on 127.0.0.1:9000
[1] pingpong http://127.0.0.1:9000/custom/pingpong
Call browse() with an index number or name to run an application.
$
And the process ends here.
Its running fine in the R shell but then i want to run it as a server on system startup.
So once the start is called , R should not exit but wait for requests on the port.
How will i convince R to simply wait or sleep rather than exiting ?
I can use the wait or sleep function in R to wait some N seconds , but that doesnt fit the bill perfectly
Here is one suggestion:
First split the example you gave into (at least) two files: One file contains the definition of the application, which in your example is the value of the app parameter to the Rhttpd$add() function. The other file is the RScript that starts the application defined in the first file.
For example, if the name of your application function is named pingpong defined in a file named Rook.R, then the Rscript might look something like:
#!/usr/bin/Rscript --default-packages=methods,utils,stats,Rook
# This script takes as a single argument the port number on which to listen.
args <- commandArgs(trailingOnly=TRUE)
if (length(args) < 1) {
cat(paste("Usage:",
substring(grep("^--file=", commandArgs(), value=T), 8),
"<port-number>\n"))
quit(save="no", status=1)
} else if (length(args) > 1)
cat("Warning: extra arguments ignored\n")
s <- Rhttpd$new()
app <- RhttpdApp$new(name='pingpong', app='Rook.R')
s$add(app)
s$start(port=args[1], quiet=F)
suspend_console()
As you can see, this script takes one argument that specifies the listening port. Now you can create a shell script that will invoke this Rscript multiple times to start multiple instances of your server listening on different ports in order to enable some concurrency in responding to HTTP requests.
For example, if the Rscript above is in a file named start.r then such a shell script might look something like:
#!/bin/sh
if [ $# -lt 2 ]; then
echo "Usage: $0 <start-port> <instance-count>"
exit 1
fi
start_port=$1
instance_count=$2
end_port=$((start_port + instance_count - 1))
fifo=/tmp/`basename $0`$$
exit_command="echo $(basename $0) exiting; rm $fifo; kill \$(jobs -p)"
mkfifo $fifo
trap "$exit_command" INT TERM
cd `dirname $0`
for port in $(seq $start_port $end_port)
do ./start.r $port &
done
# block until interrupted
read < $fifo
The above shell script takes two arguments: (1) the lowest port-number to listen on and (2) the number of instances to start. For example, if the shell script is in an executable file named start.sh then
./start.sh 9000 3
will start three instances of your Rook application listening on ports 9000, 9001 and 9002, respectively.
You see the last line of the shell script reads from the fifo which prevents the script from exiting until caused to by a received signal. When one of the specified signals is trapped, the shell script kills all the Rook server processes that it started before it exits.
Now you can configure a reverse proxy to forward incoming requests to any of the server instances. For example, if you are using Nginx, your configuration might look something like:
upstream rookapp {
server localhost:9000;
server localhost:9001;
server localhost:9002;
}
server {
listen your.ip.number.here:443;
location /pingpong/ {
proxy_pass http://rookapp/custom/pingpong/;
}
}
Then your service can be available on the public Internet.
The final step is to create a control script with options such as start (to invoke the above shell script) and stop (to send it a TERM signal to stop your servers). Such a script will handle things such as causing the shell script to run as a daemon and keeping track of its process id number. Install this control script in the appropriate location and it will start your Rook application servers when the machine boots. How to do that will depend on your operating system, the identity of which is missing from your question.
Notes
For an example of how the fifo in the shell script can be used to take different actions based on received signals, see this stack overflow question.
Jeffrey Horner has provided an example of a complete Rook server application.
You will see that the example shell script above traps only INT and TERM signals. I chose those because INT results from typing control-C at the terminal and TERM is the signal used by control scripts on my operating system to stop services. You might want to adjust the choice of signals to trap depending on your circumstances.
Have you tried this?
while (TRUE) {
Sys.sleep(0.5);
}

Writing a unix daemon

I'm trying to code a daemon in Unix. I understand the part how to make a daemon up and running . Now I want the daemon to respond when I type commands in the shell if they are targeted to the daemon.
For example:
Let us assume the daemon name is "mydaemon"
In terminal 1 I type mydaemon xxx.
In terminal 2 I type mydaemon yyy.
"mydaemon" should be able to receive the argument "xxx" and "yyy".
If I interpret your question correctly, then you have to do this as an application-level construct. That is, this is something specific to your program you're going to have to code up yourself.
The approach I would take is to write "mydaemon" with the idea of it being a wrapper: it checks the process table or a pid file to see if a "mydaemon" is already running. If not, then fork/exec your new daemon. If so, then send the arguments to it.
For "send the arguments to it", I would use named pipes, like are explained here: What are named pipes? Essentially, you can think of named pipes as being like "stdin", except they appear as a file to the rest of the system, so you can open them in your running "mydaemon" and check them for inputs.
Finally, it should be noted that all of this check-if-running-send-to-pipe stuff can either be done in your daemon program, using the API of the *nix OS, or it can be done in a script by using e.g. 'ps', 'echo', etc...
The easiest, most common, and most robust way to do this in Linux is using a systemd socket service.
Example contents of /usr/lib/systemd/system/yoursoftware.socket:
[Unit]
Description=This is a description of your software
Before=yoursoftware.service
[Socket]
ListenStream=/run/yoursoftware.sock
Service=yourservicename.service
# E.x.: use SocketMode=0666 to give rw access to everyone
# E.x.: use SocketMode=0640 to give rw access to root and read-only to SocketGroup
SocketMode=0660
SocketUser=root
# Use socket group to grant access only to specific processes
SocketGroup=root
[Install]
WantedBy=sockets.target
NOTE: If you are creating a local-user daemon instead of a root daemon, then your systemd files go in /usr/lib/systemd/user/ (see pulseaudio.socket for example) or ~/.config/systemd/user/ and your socket is at /run/usr/$(id -u)/yoursoftware.sock (note that you can't actually use command substitution in pathnames in systemd.)
Example contents of /lib/systemd/system/yoursoftware.service
[Unit]
Description=This is a description of your software
Requires=yoursoftware.socket
[Service]
ExecStart=/usr/local/bin/yoursoftware --daemon --yourarg yourvalue
KillMode=process
Restart=on-failure
[Install]
WantedBy=multi-user.target
Also=yoursoftware.socket
Run systemctl daemon-reload && systemctl enable yoursoftware.socket yoursoftware.service as root
Use systemctl --user daemon-reload && systemctl --user enable yoursoftware.socket yoursoftware.service if you're creating the service to run as a local-user
A functional example of the software in C would be way too long, so here's an example in NodeJS. Here is /usr/local/bin/yoursoftware:
#!/usr/bin/env node
var SOCKET_PATH = "/run/yoursoftware.sock";
function errorHandle(e) {
if (e) console.error(e), process.exit(1);
}
if (process.argv[0] === "--daemon") {
var logFile = require("fs").createWriteStream(
"/var/log/yoursoftware.log", {flags: "a"});
require('net').createServer(errorHandle)
.listen(SOCKET_PATH, s => s.pipe(logFile));
} else process.stdin.pipe(
require('net')
.createConnection(SOCKET_PATH, errorHandle)
);
In the example above, you can run many yoursoftware instances at the same time, and the stdin of each of the instances will be piped through to the daemon, which appends all the stuff it receives to a log file.
For non-Linux OSes and distros without systemd, you would use the (typically shell-scripted) startup system to begin your process at boot and the user would receive an error like could not connect to socket /run/yoursoftware.sock when something goes wrong with your daemon.

How do I change multiple unix passwords in one script/batch file?

I connect to 8 different unix servers from Windows, using connection type 'SSH' in putty. I use the same username/password for each server.
Currently when I need to change passwords (every 60 days), I need to open putty, select the session I want to connect to, type my current password (in the putty window that opens), type "passwd", enter my current password, and then enter my new password.
Then I exit and repeat the process 7 times.
How can I convert this to an automated process where I simply need to supply a script/batch process with my old and new password?
Here is how I automated the process:
Download and install ActiveTCL Community Edition (download the 32 bit version, even if you are on 64 bit windows, as the 64 bit version does not have "Expect" which is what you need to run the automated script)
Open the tclsh85 executable that was created by the install
Run this command "teacup install Expect" (note, this is case sensitive. You may need to setup special http settings if you receive an error and/or are on vpn or using a proxy)
Download Putty's "plink.exe" and either place it in the bin directory of ActiveTCL (default install directory is "C:\Tcl\bin") or alter your "Path" environment variable to include the path to this executable (wherever you downloaded plink.exe). This is the command-line version of Putty which your script will use.
Anywhere on your drive, create a text file named "servers.txt" with a list of the servers (one per line). They should all share the same password, as the script will login to all of them with the same password (that you supply), and change the password to the one you supply.
In the same directory as "servers.txt" create a new text file called "ChangePassword.tcl" (or whatever you want to call it, but be sure its file type is "tcl"). Right click the file and edit in notepad (or whatever text editor you prefer) and paste this script in it.
package require Expect
exp_log_user 0
set exp::nt_debug 1
proc changepw {host user oldpass newpass} {
spawn plink $host
log_user 0
expect {
"login as: " { }
}
exp_send "$user\r"
expect "sword: "
exp_send "$oldpass\r"
expect "\$ "
exp_send "passwd\r"
expect "sword: "
exp_send "$oldpass\r"
expect "sword: "
exp_send "$newpass\r"
expect "sword: "
exp_send "$newpass\r"
set result $expect_out(buffer)
exp_send "exit\r"
return $result
}
label .userlbl -text "Username:"
label .oldpasslbl -text "\nOld Password: "
label .newpasslbl -text "\nNew Password: "
set username "username"
entry .username -textvariable username
set oldpassword "oldpassword"
entry .oldpassword -textvariable oldpassword
set newpassword "newpassword"
entry .newpassword -textvariable newpassword
button .button1 -text "Change Password" -command {
set fp [open "servers.txt" r]
set file_data [read $fp]
close $fp
set data [split $file_data "\n"]
foreach line $data {
.text1 insert end "Changing password for: $line\n"
set output [changepw $line $username $oldpassword $newpassword]
.text1 insert end "$output\n\n"
}
}
text .text1 -width 50 -height 30
pack .userlbl .username .oldpasslbl .oldpassword .newpasslbl .newpassword .button1 .text1
Save the script and then launch the ChangePassword.tcl file.
Here is a picture of what it looks like when you open the ChangePassword.tcl file:
The rest should be self explanatory. Note the program does not output when your password change was successful but it will tell you when it fails. Also note, this was my first tcl script (and first time using Expect) so the script is by no means "optimized" and could probably be improved but it gets the job done. Feel free to edit, or make suggestions/improvements.
Sounds like you want Expect, an extension of TCL that can mimic typing at a keyboard for a console application. See the examples for how to do this.
Now there is something you've written that worries me:
I connect to 8 different unix servers, using connection type 'SSH' in putty. I use the same username/password for each server.
Why aren't you using SSH keys for automating the logon?
Great article! Just elaborating on step-3. Please note the commands to provide Proxy server information in case "teacup install Expect" fails due to connectivity issue:
%teacup install Expect
Resolving Expect ... Not found in the archives.
...
Aborting installation, was not able to locate the requested entity.
child process exited abnormally
% teacup list teacup
0 entities found
Problems which occurred during the operation:
* http://teapot.activestate.com :
{connect failed connection refused} {can't read
"state(sock)": no such element in array while executing
"fileevent $state(sock) writable {}"} NONE
% teacup proxy "abcproxy.mycorp.com" 8080
Proxying through abcproxy.mycorp.com # 8080
% set http_proxy_user MyNetworkID
MyNetworkID
% set http_proxy_pass MyNetworkPassword
MyNetworkPassword
% teacup list teacup
entity name version platform
----------- ------ --------------- ----------
application teacup 8.5.16.0.298388 win32-ix86
----------- ------ --------------- ----------
1 entity found
% teacup install Expect
Resolving Expect ... [package Expect 5.43.2 win32-ix86 # http://teapot.activestate.com]
Resolving Tcl 8.4 -is package ... [package Tcl 8.6.1 _ ... Installed outside repository, probing dependencies]
Retrieving package Expect 5.43.2 win32-ix86 ...# http://teapot.activestate.com ...
Ok
Installing into C:/app/Tcl/lib/teapot
Installing package Expect 5.43.2 win32-ix86
%

Monit failing to start process

I have a simple script that I'm trying to get Monit to monitor. After some digging around I found this little nugget: start program = "su - myuser -c '/home/user/myscript.rb start' " which I believe should work but looking at the log files it says:
[PDT Oct 30 02:47:17] info : 'simple_script' start: su
[PDT Oct 30 02:47:17] error : Error: Could not execute su
Likewise earlier attempts only seem to read the part preceding a space so:
start program = "/home/user/.rvm/rubies/ruby-1.9.2-p290/bin/ruby /home/user/simple_script_daemon.rb stop"
results in…
[PDT Oct 30 03:09:49] info : 'simple_script' start: /home/user/.rvm/rubies/ruby-1.9.2-p290/bin/ruby
Which doesn't fail like the first example but still seems to only execute the part preceding the space.
This is my full statement:
check process simple_script
with pidfile /home/user/simple_script.rb.pid
start program = "su - user -c '/home/user/simple_script_daemon.rb start' "
stop program = "su - user -c '/home/user/simple_script_daemon.rb stop' "
group simple_script
If you've got an idea what might be going on I'd love to hear from you!
I guess you could try something like:
check process simple_script
with pidfile /home/user/simple_script.rb.pid
start program = "/home/user/simple_script_daemon.rb start" as uid user and gid user
stop program = "/home/user/simple_script_daemon.rb stop" as uid user and gid user
group simple_script
as stated in monit doc.
You may need to provide full path to su, i.e. /bin/su

Resources