June 22, 2007

Shell commands from classical ASP

If for some reason your boss think its a good idea to make shell calls from ASP, this web page may help you, regardless of whether or not you feel compelled to agree to his ridiculous demands. Certainly there are right a proper uses for this technology but we are not here to judge, only to code.

Its actually quite simple. Three easy lines to remember. First we create the object. The object we want to create is the scripting object called wscript.shell.


set wshell = server.createobject("wscript.shell")



The next line is the command we want to run. Let's not worry ourselves with silly scripts and what have you. Put whatever you want to execute in an EXE or BAT and call it.


intReturn = wshell.run("c:\myCommand.exe")


Lastly a good programmer releases whatever he/she has taken.


set wshell = nothing


Here are the 5 important notes which you must know:


  1. If you enclose your asp scripting with anything other than <% ... %> you may be in trouble.

  2. The process runs under IWAM_MachineName. If your computer is called ServerA, That user is IWAM_ServerA. Thus you must give that user execute rights to your executing exe which is myCommand.exe and other other resource it uses such as folders and log files.

  3. IWAM_MachineName is hardly ever logged on interactively (like never) so don't expect code such as MsgBox("Hello world") or running notepad.exe to work. It won't!

  4. The exe runs asynchronously. Which means line 2 returns before the application has finished running. Probably there's a way for the thread to wait but I can't be bothered about that now.

  5. Running shells in a webserver is .... not advisable. Don't try it unless you know what you're doing and even if you are I'm not responsible if your webserver gets hacked.



Thanks and goodnight

June 04, 2007

Web services for C

I won't get into a long preamble about what a web service is. Just one paragraph, you can skip to the next paragraph if you like. Web services are among the easiest things to code. Usually they are simple function calls with primitive input and output data types. Doing anything more complex is counter-productive. It's meant to be something that can process something and return the result almost immediately. So by design web services should be simple.

I was tasked to modify an existing application - written in C to make web service calls from an existing Java based web service deployed using Sun Application Server PE9. In VB.NET. I could do this in 2 minutes. In C on Linux, it takes a bit longer, and that is even after knowing what to do. I took me half a day (6 hours) to get to work. But with this article, you can do it in 5 minutes. Let's begin.

I used GSoap. You can use something else (like Apache Axis2) but then you'd have to stop reading this article because I can't help you. GSoap has documentation and I read it, most of it but the document is badly written. Most documentation is badly written that's why I have this blog. To redocument the badly documented projects. Oh by the way you should start downloading GSoap so that it will be fully downloaded by the time you finish reading about how its documentation sucks. Oh by the way, the files are hosted on source forge with requires you to click here and there and the download link is too tiny to see and contains no useful information anyway. So click here It gets you a few clicks closer to downloading GSoap.

I'll divide the guide into three parts. Pre setup, Setup and Post setup. Clear?

Pre Setup

Install it somewhere Doesn't really matter where. That means:


mkdir somewhere
cd somewhere
gunzip gsoap_linux_2.7.9f.tar.gz
tar xvf gsoap_linux_2.7.9f.tar


I'll only show you the client stuff. Maybe its trivial but if it was why did I spend 6 hours getting it to work? Make a working directory to put all your crap. That means:

mkdir working
cd working


Copy the following files from the gsoap directory to your working directory.

gsoap-linux-2.7/bin/wsdl2h
gsoap-linux-2.7/bin/soapcpp2
gsoap-linux-2.7/stdsoap2.c
gsoap-linux-2.7/stdsoap2.h


Setup

That's it for presetup. We're ready to setup. You need the WSDL file. Hopefully its hosted on a webserver somewhere. There's a url to it. I'm not going to show the WSDL file. Its proprietary so bite me.

http://192.168.1.21:8080/myWeb/myWebService?wsdl


So we run wsdl2h. The -c argument generates c code. myWeb.h is a header file which is read by the other preprocessor soapcpp2. This generates myWeb.h as you can imagine.

cd working
./wsdl2h -c -o myWeb.h http://192.168.1.21:8080/myWeb/myWebService?wsdl


Now go open myWeb.h in your favourite text editor. Around line [165] you should see something like

//gsoap ns1 service name: long_and_painful_service_name_generated_by_stupid_computer_code

Replace this with

//gsoap ns1 service name: myWeb


Its time to generate a whole bunch of other files which you never knew you needed. Here the -c argument tells it to generate c code, or that you want client stuff. Regardless, just use -c, thanks.

./soapcpp2 -c myWeb.h


Post Setup
Now you have a whole bunch of files in your working directory. Aren't you glad you used a working directory instead of your home directory or the gsoap bin directory? The last step is to write the client. Create your client.c file and import your usual libraries.

#include <stdio.h>
#include <stdlib.h>
#include "soapH.H"
#include "myWeb.nsmap"


myWeb.nsmap already includes soapH.h but that's beside the point. You could do away with myWeb.nsmap if you want by including its contents in the main file but I guess the idea is to interchange the nsmap. Details! Bottomline is that soapH.h does all the work. nsmap is the configuration file for 'some' things. More code...

Write your main function and a simple hello world. Compile it to make sure all the headers and libraries are linked.

int main
(int argc,char** argv) {
printf("Soap client");
}


Here's the compile command in case you are new to c. I was.

gcc -o myWebClient.bin client.c stdsoap2.c soapC.c soapClient.c


myWebClient.bin is the executable to be generated. Run it using

./myWebClient.bin

stdSoap2.c you copied from the gsoap folder in the presetup phase. soapC.c and soapClient.c you generated using soapcpp2 in the setup phase. More code...


// create context
struct soap * soap = soap_new();

//do stuff

// clean up
soap_destroy(soap);
soap_end(soap);
soap_done(soap);


You could use soap instead of soap * but lets not. It would mean using &soap when calling destroy, end and done. I hate pointers.

In the do stuff section we define our input and output parameters.

struct ns2__myFunctionResponse response;
struct ns2__myFunction request;


If your function has input parameters, set them up now. I don't so I completely made this code up. The request variable is a struct so we deference its members using the dot notation. C experts are laughing at me.

request.param1 = 1;


Call your function

if ((soap_call___ns1__myFunction( soap, NULL, NULL, &request, &response)) == SOAP_OK) {
printf("Success");
} else {
soap_print_fault(soap, stderr);
}

Note 1: The first NULL can be replaced with the web service end point. Sometimes the WSDL file will contain the hostname rather than the IP and your client computer can't resolve the hostname, so you better check and put in ip-based endpoint instead of NULL.
Note 2: The second NULL is the soap action. I don't know what this value should be so we set it to default based on the WSDL.
Note 3: If your response has any values to return, you can reference them using

response.param1

Note 4: This code might not work yet.

Now that we have the client set up, let's spend a few hours trying to figure out why it doesn't work. Changing the endpoint through a proxy lets us view the request and response. Changing the request and pushing it through telnet lets us test possible reasons why it doesn't work. Coding a new client in VB.NET and analysing why IT works but our gsoap client doesn't also helps. Now we have the answer. gsoap uses a particular version of soap (Soap 1.2) but our application server, Sun Application Server PE9 only accepts an earlier version of soap (Soap 1.1). So we edit myWeb.nsmap to tell the gsoap toolkit to use soap 1.1 and who's your daddy.

Replace the similar looking lines in myWeb.nsmap with this.

{"SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/", "http://www.w3.org/2002/06/soap-encoding"},
{"SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/", "http://www.w3.org/2002/06/soap-envelope"},

See this nugget of documentation for more information.

Thats it! A working Linux-C webservice client. It might take 5 minutes to run this tutorial but it took me 2 hours to write and 6 hours to research. If you'd like to help me out. Paypal me some money.