Linux: How to make a daemon/service usable with xinetd? - linux

Anybody knows what changes are necessary for a server to work with xinetd ?
The server being a .NET mailserver that runs on Linux.
See the bottom of this post for reference:
Lumisoft Mailserver Forum Post
Note: xinetd, not mono-service. [x]inetd is an internet superserver.
A superserver starts a server service on demand.
(As opposed to the server service running continuously, which is what mono-service does)

An inetd service runs differently from a standalone server. inetd services read stdin and write to stdout, letting inetd handle the gory details of TCP/IP, rather than keeping track of their own sockets. If you want to make a server run under inetd, it'll have to do the same.
The following program runs just fine under xinetd on my machine:
#include <iostream>
#include <string>
using namespace std; // yeah, i'm lazy.
int main()
{
string name;
cout << "What's your name? " << flush;
cin >> name;
cout << "Hi, " << name << "!" << endl;
}
Note i'm not at all worried about sockets -- xinetd arranges things so that the service can read standard input and write to standard output. You just write your app like you'd be running it on the console, for the most part. The socket details are specified in the config file for the service. (Note, you might be able to get/set details about the socket using stdin/stdout, which may be the actual socket -- i'm not sure -- but you really should leave that stuff up to inetd.)

An inetd services are really great for one off apps that need to take in data and act with some degree of interaction with the user. IT works over tcp/udp by piping the data viva a socket from (x)inetd to std{in,out,err}. inetd apps also works well with tcpwrappers to inhance security though system policy files and ACL.
So yes you would write your app like its a console app since in reality it is a console app. Just think of inetd as a transparent reverse proxy from the network to your app's inputs.
A Word of advice, write your code to handle the process signals correctly and if you need to interact with another process on the system use unix sockets/fifo for that.
Also, don't try to write an app that streams a lot of data all at once or needs a lot of connections. Scalability is an issue as inetd becomes a bottle neck, this is why Apache and Sendmail dropped support for inetd and sit as mono apps instead. HTTP fits this role better and a fastcgi (or insert favorite framework) script with nginx works best for that use case.
A good example for an inetd would be:
lock = Mutex.new
trap :HUP { #log the connection and cleanup }
trap :USR1 { lock.synchronize do #stuff; end }
trap :TERM { #clean up }
trap :KILL { #clean up and die with error codes }
puts "App name - version"
loop do
('%s> ' % Console.prompt).display
input = gets.chomp
command, *params = input.split /\s/
case command
when /\Ahelp\z/i
puts App.help_text
when /\Ado\z/i
Action.perform *params
when /\Aquit\z/i
exit
else
puts 'Invalid command'
end
end
exit
Edit your /etc/services to include your app like this:
myapp port#/proto
and add your app to /etc/inetd.conf (or xinetd.d) like this:
myapp stream tcp6 nowait myappuser /path/to/myapp myapp -arg_flags

Related

Using the Hyper-V sockets between Windows host and Linux guest

I want to write simple application that communicates between the Hyper-V host and its virtual machine using Hyper-V sockets (netcat over vsock). In the Internet there are a few documents describing how to do it: Make your own integration services, Practical Hyper-V socket communication. However, any of them helps me to achieve my goal.
First of all, I've made sure that the connection using Hyper-V sockets is possible. On the guest Linux I loaded hv_sock module and run nc-vsock application which is able to listen on vsocks:
$ sudo modprobe hv_sock
$ nc-vsock -l 1234
On Windows in PowerShell I ran hvc, which utilises Hyper-V sockets and is able to emulate netcat:
hvc nc -t vsock little-helper 1234
and it works. I can see data sent from server to client and vice versa.
Then I wrote a simple application basing on 1 and 2 with slight changes.
I registered my application with the Hyper-V Host's registry as said in 1 and I ran my application. The connection was not established and the connect function returned error 10049.
I've tried to run my application as a administrator and manipulate GUIDs in the source code and on the Hyper-V Host's registry as well. However, nothing helps and application always reports error 10049.
In my opinion in the document are some ambiguity. E.g. it's said that the service id shall be a random GUID. But later on there is a note that the first four octets translate to port in AF_VSOCK address family, and the specific GUID is presented for this purpose.
Question is rather simple: what I did wrong or misunderstood. Is it possible to write netcat utilising vsock between Windows and Linux?
Full code:
#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <hvsocket.h>
#include <combaseapi.h>
int main()
{
struct __declspec(uuid("00000000-185c-4e04-985a-4c2eee3e03cc")) VSockTemplate {};
struct __declspec(uuid("2a9fa68e-4add-45cb-85c8-de97fc66d388")) ServerVsockTemplate {};
//----------------------
// Initialize Winsock
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != NO_ERROR) {
wprintf(L"WSAStartup function failed with error: %d\n", iResult);
return 1;
}
//----------------------
// Create a SOCKET for connecting to server
SOCKET ConnectSocket;
ConnectSocket = socket(AF_HYPERV, SOCK_STREAM, HV_PROTOCOL_RAW);
if (ConnectSocket == INVALID_SOCKET) {
wprintf(L"socket function failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
//----------------------
// The sockaddr_in structure specifies the address family,
// IP address, and port of the server to be connected to.
SOCKADDR_HV clientService;
clientService.Family = AF_HYPERV;
clientService.VmId = __uuidof(ServerVsockTemplate);
clientService.ServiceId = __uuidof(VSockTemplate);
clientService.ServiceId.Data1 = 1234;
//----------------------
// Connect to server.
iResult = connect(ConnectSocket, (SOCKADDR*)&clientService, sizeof(clientService));
if (iResult == SOCKET_ERROR) {
wprintf(L"connect function failed with error: %ld\n", WSAGetLastError());
iResult = closesocket(ConnectSocket);
if (iResult == SOCKET_ERROR)
wprintf(L"closesocket function failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
wprintf(L"Connected to server.\n");
iResult = closesocket(ConnectSocket);
if (iResult == SOCKET_ERROR) {
wprintf(L"closesocket function failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
WSACleanup();
return 0;
}
For whoever is not very familiar with Hyper-V and does not want to spend 2 hours debugging, like I did, a few points:
Ensure that the hv_sock kernel module is enabled on the guest, I used Ubuntu Server 20.04, which does not have this enabled by default.
lsmod | grep hv_sock
If it's not there you need to add it and reboot:
sudo sh -c 'echo "hv_sock" > /etc/modules-load.d/hv_sock.conf'
sudo reboot
You need to register a new application with Hyper-V Host's registry, but the docs are misleading, as the random GUID is only needed with a Windows guest, for Linux guests the GUID needs to be in a very specific format, as described by HV_GUID_VSOCK_TEMPLATE, meaning <port>-facb-11e6-bd58-64006a7986d3
So for port 5001 the registry key should be 00001389-facb-11e6-bd58-64006a7986d3 (1389 is 5001 in hex)
You can do that easily from powershell as described in the Register a new application section
$service = New-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\GuestCommunicationServices" -Name "00001389-facb-11e6-bd58-64006a7986d3"
$service.SetValue("ElementName", "HV Socket Demo")
You can find some simple code samples here, win_server.c for the Windows host and wsl_client.c for the Linux guest.
What is the ServerVsockTemplate GUID you have there? I'm pretty sure that's supposed to be the GUID of a running VM, e.g., (Get-VM -Name $VMName).Id, so it'd be hard to hard-code into your source. If that's the GUID you generated per 'Register a new application', that'll be the problem.
The docs aren't clear, but I have the strong suspicion that 'Register a new application' part is only for when you're listening on Windows for incoming connections from other Windows VMs, or when writing a Linux device driver that talks to a service on the host. It might also be to allow a VM to offer services to other VMs, but I would assume not.
Edit: Quick Testing VSOCK (Hyper-V) Support in X410 says you need to make the registry keys for the vsock GUIDs as well, to receive connections from the vm.
In Linux userspace, you only have access to vsock; the other services are managed by drivers under Linux.
There's a clearer explanation of the vsock workflow in the Linux source for the Hyper-V vsock implementation.
I assume it's possible to use VSock between a Windows VM and the host as well, of course.
Edit, because I actually went and tested this.
Two mistakes in the code, on top of the ServerVsockTemplate GUID needing to be the GUID of the target VM.
The VSockTemplate GUID is wrong. I don't know where that came from, but there's a constant HV_GUID_VSOCK_TEMPLATE in <hvsocket.h> anyway, which matches the one on the Microsoft Docs site: 00000000-facb-11e6-bd58-64006a7986d3
It turns out, you need to zero the Reserved member of the SOCKADDR_HV, or it'll fail. Traditionally, one would use memset to zero a new sockaddr_* structure, but in this case, we can take the easy path.
So to make this work, change the SOCKADDR_HV creation code to the following:
// The sockaddr_in structure specifies the address family,
// IP address, and port of the server to be connected to.
SOCKADDR_HV clientService;
clientService.Family = AF_HYPERV;
clientService.Reserved = 0;
clientService.VmId = __uuidof(ServerVsockTemplate);
clientService.ServiceId = HV_GUID_VSOCK_TEMPLATE;
clientService.ServiceId.Data1 = 1234;
Then you delete VSockTemplate, and make sure ServerVsockTemplate is the GUID of the VM or Micro-VM where you're running nc-vsock.
I actually tested this with the WSL2 micro-VM, for which the VM ID comes from hcsdiag list rather than Get-VM, but I was able to connect to nc-vsock inside my WSL2 session using the source here, modified as I have described.

NodeJS. Child_process.spawn. Handle process' input prompt

I'm currently working on my web interface for git. Accessing git itself by child_process.spawn. Everything is fine while there is simple "command -> response" mechanism, but I cannot understand what should I do with command prompts (git fetch asks for password for example). Hypothetically there is some event fired, but I don't know what to listen to. All I see is "git_user#myserver's password: _" in command line where node.js process itself is running.
It would be great to redirect this request into my web application, but is it even possible?
I've tried to listen on message, data, pipe, end, close, readable at all streams (stdout, stdin, stderr), but no one fires on password prompt.
Here is my working solution (without mentioned experiments):
var out="";
var err="";
var proc=spawn(exe,cmd);
proc.on("exit",function(exitCode){
});
proc.stdout.on("data",function(data){
out+=data;
});
proc.stderr.on("data",function(data){
err+=data;
});
proc.on("close",function(code){
if(!code)func(out);
else return errHandler(err);
});
Can you please help me with my investigations?
UPDATE
Current situation: on my GIT web interface there is a button "FETCH" (as an example, for simple "git fetch"). When I press it, http request is generated and being sent to node.js server created by http.createServer(callback).listen(8080). callback function receives my request and creates child_process.spawn('git',['-C','path/to/local/repo','fetch']). All this time I see only loading screen on my web interface, but if I switch to command line window where node script is running I will see a password prompt. Now let's pretend that I can't switch window to console, because I work remotely.
I want to see password prompt on my web interface. It would be very easy to achieve if, for instance, child_process would emit some event on child.stdin (or somewhere else) when prompting for user input. In that case I would send string "Come on, dude, git wants to know your password! Enter it here: _______" back to web client (by response.end(str)), and will keep on waiting for the next http connection with client response, containing desired password. Then simply child.stdin.write(pass) it to git process.
Is this solution possible? Or something NOT involving command line with parent process.
UPDATE2
Just tried to attach listeners to all possible events described in official documentation: stdout and stderr (readable, data, end, close, error), stdin (drain, finish, pipe, unpipe, error), child (message, exit, close, disconnect, message).
Tried the same listeners on process.stdout, process.stderr after piping git streams to it.
Nothing fires on password request...
The main reason why your code wont work is because you only find out what happened with your Git process after is what executed.
The major reason to use spawn is beacause the spawned process can be configured, and stdout and stderr are Readable streams in the parent process.
I just tried this code out and it worked pretty good. Here is an example of spawning a process to perform a git push. However, as you may know git will ask you for username and password.
var spawn = require('child_process').spawn;
var git = spawn('git', ['push', 'origin', 'master']);
git.stderr.on('data', function(data) {
// do something with it
});
git.stderr.pipe(process.stderr);
git.stdout.pipe(process.stdout);
Make a local git repo and setup things so that you can do the above push command. However, you can really do any git command.
Copy this into a file called git_process.js.
Run with node git_process.js
Don't know if this would help but I found the only way to intercept the prompts from child processes was to set the detached option to true when you spawn a new child process.
Like you I couldn't find any info on prompts from child process in node on the interwebs. One would suspect it should go to stdout and then you would have to write to stdin. If I remember correctly you may find the prompt being sent to stderr.
Its a bit amazing to me that others haven't had this problem. Maybe we just doing it wrong.

Continuous Execution of commands in Linux using Ganymed SSH

I am using Ganymed SSH to execute commands in my Linux Machine from Java application. I have to execute 100 commands one by one.
For this in my code under for loop I am opening and closing the session for each iteration. But it is throwing some exception after 10 or 15 iterations saying that Unable to establish the connection.
I believe this is happening because the sessions are not creating frequently.
Please check the below code and guide me some good solution for achieve this.
for(String user : usersList)
{
session = connection.openSession();
session.execCommand("[ -f /home/"+user+"/.file_name] && echo \"Found\" || echo \"Not Found\"");
session.close();
}
Thanks & Regards,
Gupta Katakam
If you try to do this, you will end up in the situation that you are in. SSH has many configuration parameter but that you need to specify in the end machine. I am not sure if you have access to the end machine or not, but in case you do not have access to the target machine, please consider adding some delay on you code after doing a disconnect and re-connect. I was also facing the same problem although I was using a different library. Let me know if this things works or not, and then we can take it forward from there.

Send command to running node process, get back data from inside the app

I start a node.js app per commandline in linux.
I see the app running, e.g. by entering "top".
Is there a way to send some command to the running app (maybe to the pid?) and get back info from inside it (maybe listen for some input and return requested info)?
Use repl module. There are examples in the doco doing exactly what you need: run JS in the context of your application and return output.
One simple solution is to use process signals. You can define a handler for a signal in your program to output some data to the console (or write to a file or to a database, if your application is running as a service not attached to a terminal you can see):
process.on('SIGUSR1', function() {
console.log('hello. you called?');
});
and then send a signal to it from your shell:
kill --signal USR1 <pid of node app.js>
This will invoke the signal handler you have defined in your node.js application.

Is it possible to create a proper interactive shell over http?

I want to have a shell access over http to interact with a program running on my server (as opposed to SSH and other protocol). I have done some research and found two main ways, the php way such as http://sourceforge.net/projects/phpterm/ and the CGI way. Although these result in shell-like terminals over http, I can't interact with programs with standard input/output without passing paramaters at run: ./prog -options etc..
With a standard shell over netcat for example ./prog would provide full interaction so that it could prompt for input etc..
The test program I am running is:
#include<stdio.h>
#include<sys/types.h>
include<stdlib.h>
int main ()
{
// set up keyword(passcode)
char this[14];
char that[128];
// check the password and exit if it doesn't match;
fgets(this, 14, stdin);
if (strncmp(this, "passwd\n", 14)) {
exit(0);
}
printf("shell interaction success! \n");
fgets(that, 128, stdin);
system(that);
exit(0);
}
If run from netcat this would occur:
./prog
passwd
Shell interaction success
If run from other shell like solutions over http I have come accrss:
./prog
then nothing.
Fingers crossed someone knows how!
You could do this, but since HTTP is a connectionless request-response protocol, it wouldn't use just one HTTP connection.
A browser would make a request to start a shell on the remote server
A backend service would be started that creates the desired process and captures the stdin/stdout pipes
Javascript on the browser would send (POST perhaps) a request to the server to say "this user typed some character"
Some kind of AJAX request polling loop would get new output from the backend process and display it on the browser
Or, this could be considerably simplified using WebSockets which is a stream protocol (and is implemented by some browsers but is not HTTP).

Resources