Monday, 8 September 2014

system log tools syslog / rsyslog

In Red Hat Enterprise Linux 3/4/5, the default system log tool is syslogd which is provided by package sysklogd, but since Red Hat Enterprise Linux 6, the rsyslogd became the default. rsyslog package is also provided since Red Hat Enterprise Linux 5.2. 

Syslog is the most common method used for computer message logging. It is the foundation for most security auditing applications. Syslog stores all the messages from every device on the network in a local repository for use by a network management system to give the administrator a network-wide view from a single station.

Syslog’s architecture is based upon the concept of messages and facility codes. The model is a Client/Server with the clients (logging hosts) sending messages up to a max 1024 bytes to the syslog server using either TCP or UDP on port 514.

A message is labeled with an indicator as to which one of the software types generated the message. These are actually simple categories syslog defines to allow it to handle messages differently and specifically for each type.

They are identified by keywords: auth, authpriv, cron, daemon, FTP, lpr, kern, mail, news, syslog, user, uucp local0 … local7. 

The selector field itself again consists of two parts, a facility and a priority, separated by a period (``.'').

The facility is one of the following keywords: auth, authpriv, cron, daemon, kern, lpr, mail, mark, news, security (same as auth),syslog, user, uucp and local0 through local7.

There is no way to define your own facilities but there are many predefined ones (up to 23 in all, depending on which syslog you use):

auth (Security events get logged with this)

authpriv (user access messages use this)

cron (for cron, at, and anacron, but not for the programs started by cron)

daemon (other daemon programs without a facility of their own)

kern (kernel messages)

lpr (print system)

mail

mark (used by syslogd to produce timestamps in log files)

news

syslog

user (for user programs)

uucp (obsolete form of networking)

local0 – local7 (any use; RH uses local7 for boot messages)

* (for all)

The priority is one of the following keywords, in ascending order: debug, info, notice, warning, warn (same as warning), err, error (same as err), crit, alert, emerg, panic (same as emerg). The keywords error, warn and panic are deprecated and should not be used anymore. 

 The priority defines the severity of the message.

The priority is one of the following eight levels, which are ranked in order from high to low priority:

emerg
alert
crit
err
warning
notice
info
debug (or “*”)
An asterisk (``*'') stands for all facilities or all priorities, depending on where it is used (before or after the period). The keyword none stands for no priority of the given facility.

You can specify multiple facilities with the same priority pattern in one statement using the comma (``,'') operator. 

Multiple selectors may be specified for a single action using the semicolon (``;'') separator. 

Remember that each selector in the selector field is capable to overwrite the preceding ones. 

Using this behavior you can exclude some priorities from the pattern.

You may precede every priority with an equation sign (``='') to specify only this single priority and not any of the above.  You may also (both is valid, too) precede the priority with an exclamation mark (``!'') to ignore all that priorities, either exact this one or this and any higher priority. If you use both extensions than the exclamation mark must occur before the equation sign, just use it intuitively.

You may prefix each entry with the minus ``-'' sign to omit syncing the file after every logging. Note that you might lose information if the system crashes right behind a write attempt. Nevertheless this might give you back some performance, especially if you run programs that use logging in a very verbose manner.

Usually critical messages are also directed to ``root'' on that machine. You can specify a list of users that shall get the message by simply writing the login. 

You may specify more than one user by separating them with commas (``,''). If they're logged in they get the message. Don't think a mail would be sent, that might be too late.

Emergency messages often go to all users currently online to notify them that something strange is happening with the system. To specify this wall feature use an asterisk (``*'').

When specifying a priority, that and all higher ones are selected too.  A selector is one or more facilities (separated by commas), a dot, then the priority.  More complex selectors are possible too; one such is shown below.)  Some example selectors:

mail.*       mail facility, any priority

mail.debug   mail facility, debug or higher priority (same as *)

mail,news.*  all messages from mail or news

auth.warning all security messages of warning or higher priority

*.info       all messages from any facility except debug msgs

*.=info      any facility, info msgs only (and not higher)

*.!err       any facility, pri <= err only

*.!=alert    any facility, any priority except alert

*.info;mail,news,authpriv.none
             all msgs with info or higher priority except
             mail, news, and authpriv

That last one is tricky.  Using multiple selectors on a single line this way allows you to specify a general category first, then for the matching log messages you can specify exceptions.  Always go from most general selector to most specific or your setup may not log what you think it should!.

Examp:

Here are some example,  
# Store critical stuff in critical
#

*.=crit;kern.none            /var/adm/critical

This will store all messages with the priority crit in the file /var/adm/critical, except for any kernel message.

# Kernel messages are first, stored in the kernel
# file, critical messages and higher ones also go
# to another host and to the console
#

kern.*                       /var/adm/kernel
kern.crit                    @finlandia
kern.crit                    /dev/console
kern.info;kern.!err          /var/adm/kernel-info

The first rule direct any message that has the kernel facility to the file /var/adm/kernel.

The second statement directs all kernel messages of the priority crit and higher to the remote host finlandia. 
This is useful, because if the host crashes and the disks get irreparable errors you might not be able to read the stored messages. 

If they're on a remote host, too, you still can try to find out the reason for the crash.

The third rule directs these messages to the actual console, so the person who works on the machine will get them, too.

The fourth line tells the syslogd to save all kernel messages that come with priorities from info up to warning in the file /var/adm/kernel-info. 

Everything from err and higher is excluded.

# The tcp wrapper loggs with mail.info, we display
# all the connections on tty12
#

mail.=info                   /dev/tty12

This directs all messages that uses mail.info (in source LOG_MAIL | LOG_INFO) to /dev/tty12, the 12th console. 
For example the tcpwrapper tcpd(8) uses this as it's default.

# Store all mail concerning stuff in a file
#

mail.*;mail.!=info           /var/adm/mail

This pattern matches all messages that come with the mail facility, except for the info priority. These will be stored in the file /var/adm/mail.

# Log all mail.info and news.info messages to info
#
mail,news.=info              /var/adm/info

This will extract all messages that come either with mail.info or with news.info and store them in the file /var/adm/info.

# Log info and notice messages to messages file
#
*.=info;*.=notice;\
        mail.none  /var/log/messages

This lets the syslogd log all messages that come with either the info or the notice facility into the file /var/log/messages, 
except for all messages that use the mail facility.

# Log info messages to messages file
#
*.=info;\
        mail,news.none       /var/log/messages

This statement causes the syslogd to log all messages that come with the info priority to the file /var/log/messages. But any message coming either with the mail or the news facility will not be stored.

# Emergency messages will be displayed using wall
#
*.=emerg                     *

This rule tells the syslogd to write all emergency messages to all currently logged in users. This is the wall action.

# Messages of the priority alert will be directed
# to the operator
#
*.alert                      root,joey

This rule directs all messages with a priority of alert or higher to the terminals of the operator, i.e. of the users ``root'' and ``joey'' if they're logged in.


*.*                          @finlandia

This rule would redirect all messages to a remote host called finlandia. This is useful especially in a cluster of machines where all syslog messages will be stored on only one machine.

 CONFIGURATION FILE SYNTAX DIFFERENCES

Syslogd uses a slightly different syntax for its configuration file than the original BSD sources. Originally all messages of a specific priority and above were forwarded to the log file. The modifiers ``='', ``!'' and ``-'' were added to make the syslogd more flexible and to use it in a more intuitive manner. The original BSD syslogd doesn't understand spaces as separators between the selector and the action field.
 
 Syslog Severity Levels

The message severity levels are standard and they are listed in seven levels from debug at level 7 being the least severe up to emergency at level 0. 
 
Security Level  Severity Description
--------------  ----------     ---------------
 0     Emergency System is unusable.
 1 Alert    Action must be taken immediately.
 2 Critical Critical conditions.
 3 Error Error conditions.
 4 Warning    Warning conditions.
 5 Notice    Normal but significant condition.
 6 Informational Informational messages.
 7 Debug Debug-level messages.
 
 Message Format

A Syslog message takes the form: < PRI > HEADER MSG

Length 0 – 1024 bytes

PRI – stands for priority.

<PRI > one byte
Severity level 3 bits  0-7 Facility 5 bits
 
The Priority level is calculated by (value of the Facility x 8) + severity level


Example: a facility value of kernel message = 0 x 8 + severity level 1 = priority of <1>

 Header

The header contains a timestamp, which is the time the message was generated. It also holds the IP address of the host.

 Message

Tag: – The name of the process that generated the message

Content: – The message body

Configuring Syslog on Linux

Configuring logging on a Linux system is very important as logs allow administrators to troubleshoot issues both at the application and network layers and without logs (alerts, errors and notifications) they would be working blind.

In order to configure Syslog the /etc/syslog.conf or the later /etc/rsyslog.cong file may need to be configured. 

To configure Syslog you need to deal with the relevant ‘.conf’ file for your syslog version. With rsyslog that’s rsyslog.conf and 
usually located at ‘/etc/rsyslog.conf’. The config file is where you specify things like:

The format of your log messages. what file events from the different facilities (i.e. different parts of your system) should be logged to. what file events with different severities should be logged to.

where to log your data – e.g. you can log to files, but you can also log to databases or forward your logs to a remote server/logging service.

filters – i.e. remove events that you do not want logged.

It is in this file where the logging levels and preferences are set.
 
# Sample syslog.conf file that sorts messages by
# mail, kernel, cron and “other”
# send mail, cron, and kernel/firewall msgs to
# their respective log files

mail.*                    -/var/log/mail

kern.*                    -/var/log/kernel_n_firewall

cron.*                    -/var/log/cron

# save the rest in one file

*.*;mail.none;authpriv.none;cron.none /var/log/messages 

This file consists of two columns. The first lists the facilities and severities of messages to expect and the second lists the files to which  they should be logged. By default, RedHat/Fedora's /etc/rsyslog.conf file is configured to put most of the messages in the file /var/log/messages. 
 
 Here is a sample:

*.info;mail.none;authpriv.none;cron.none        /var/log/messages

In this case, all messages of severity "info" and above are logged, but none from the mail, cron or authentication facilities/subsystems

You can make this logging even more sensitive by replacing the line above with one that captures all messages from debug severity and above in the /var/log/messages file. This example may be more suitable for troubleshooting.

*.debug                              /var/log/messages

In this example, all debug severity messages; except auth, authpriv, news and mail; are logged to the /var/log/debug file in caching mode. Notice how you can spread the configuration syntax across several lines using the slash (\) symbol at the end of each line.

 *.=debug;\
       auth,authpriv.none;\
       news.none;mail.none     -/var/log/debug
  
Here we see the /var/log/messages file configured in caching mode to receive only info, notice and warning messages except for the auth, authpriv, news and mail facilities.

*.=info;*.=notice;*.=warn;\
       auth,authpriv.none;\
       cron,daemon.none;\
       mail,news.none          -/var/log/messages

You can even have certain types of messages sent to the screen of all logged in users. In this example messages of severity emergency and above triggers this type of notification. The file definition is simply replaced by an asterisk to make this occur.

*.emerg                         *

Certain applications will additionally log to their own application specific log files and directories independent of the syslog.conf file. Here are some common examples:
Files:

/var/log/maillog             : Mail
/var/log/httpd/access_log    : Apache access logs

Directories:

/var/log
/var/log/samba                      : Samba messages
/var/log/mrtg                       : MRTG messages
/var/log/httpd                      : Apache webserver messages

Note: In some older versions of Linux the /etc/rsyslog.conf file was very sensitive to spaces and would recognize only tabs. 

The use of spaces in the file would cause unpredictable results. Check the formatting of your /etc/rsyslog.conf file to be safe.   

 Activating Changes to the syslog Configuration File

Changes to /etc/rsyslog.conf will not take effect until you restart syslog.

Managing the syslog daemon is easy to do, but the procedure differs between Linux distributions. Here are some things to keep in mind.

Firstly, different Linux distributions use different daemon management systems. Each system has its own set of commands to do similar operations. 

The most commonly used daemon management systems are SysV and Systemd.

Secondly, the daemon name needs to be known. In this case the name of the daemon is rsyslog.

Armed with this information you can know how to: Start your daemons automatically on booting Stop, start and restart them later on during troubleshooting or when a configuration file change needs to be applied.

 
 Common Linux log files names and usage

/var/log/messages : General message and system related stuff
/var/log/auth.log : Authenication logs
/var/log/kern.log : Kernel logs
/var/log/cron.log : Crond logs (cron job)
/var/log/maillog : Mail server logs
/var/log/qmail/ : Qmail log directory 
/var/log/httpd/ : Apache access and error logs directory
/var/log/lighttpd/ : Lighttpd access and error logs directory
/var/log/boot.log : System boot log
/var/log/mysqld.log : MySQL database server log file
/var/log/secure or /var/log/auth.log : Authentication log
/var/log/utmp or /var/log/wtmp : Login records file
/var/log/yum.log : Yum command log file.


Sunday, 3 August 2014

Creating a Super user with root Privileges


Sudo is a program which can be used by normal users to execute programs as super user or any other user. Sudo access is controlled by /etc/sudoers. The users listed in /etc/sudoers file can execute commands with an effective user id of 0 and a group id of root's group.

The file '/etc/sudoers' should be edited with the editor "visudo".

 First, create a user called "ctechz"
 

# useradd ctechz
# passwd
ctechz

 To give a specific group of users limited root privileges, edit the file with visudo as follows:


 # visudo

03. Go down to the line ‘# User privilege specification‘and add the following line.


ctechz ALL=(ALL) ALL

ctechz : name of user to be allowed to use sudo
ALL : Allow sudo access from any terminal ( any machine ).
(ALL) : Allow sudo command to be executed as any user.
ALL : Allow all commands to be executed.


OR

Create a user with uid 0

# useradd -u 0 -o jeff

 -u, --uid UID force use the UID for the new user account
 -o, --non-unique allow create user with duplicate
 


OR

Create a Normal user and edit its uid in /etc/passwd file and make it as ZERO

Sudo OverView


   Sudo An Overview

File : /etc/sudoers
Cmd  :  visudo

sudo allows a permitted user to execute a command as the superuser.

The sudo utility enables the users mentioned in configuration file sudoers to have temporary access to run certain commands as the “root” or any other user.

Whenever sudo command is executed by a user, it reads the sudoers file to check whether the user is permitted to run this command.

To edit the sudo parameters in sudoers file, command visudo should only be used due to the following reasons:

• Sudoers file might not have the same location on all versions of Linux.

• Visudo checks the syntax in sudoers file after saving it and will prompt for errors.

• It gives the option to reject the changes or re-edit the file

• It prevents two users from editing the file at the same time

The visudo command should be run as root
# visudo

 Syntax For sudo in sudoers file

General sudoers file record format: 

user   MACHINE=COMMANDS

user/group hostname = (runasuser) command(s)

root    ALL=(ALL)       ALL

Here,
• user/group is the name of the user or group for which sudo privileges are defined.

• hostname is a list of terminals from where user can use sudo.

• runasuser is the name of user which the sudo user is trying to act as, and must be enclosed in ( ).

• Command(s) is a list of commands that this user can execute. Complete path of the command must be specified.

# visudo

root ALL=(ALL) ALL ---- instead of root user we are adding a
   normal user to run this below command in his terminal.

jeff ALL=/etc/httpd reload

User jeff can reload httpd from any terminal

# sudo /etc/init.d/httpd reload
sudo] password for jeffin:
Reloading httpd:

We can also create aliases for:
• users/groups: User_Alias 
• run comands as other users: Runas_Alias
• hostname: Host_Alias
• command: Cmnd_Alias

Eg:
User_Alias USER = user1, user2
Runas_Alias PRIVUSER = root, jeff
Host_Alias SEGMENT = 192.168.1.0/24
Cmnd_Alias SERVICES = /sbin/service, /sbin/chkconfig

Check the following example for Aliasing. For a particular user to run particular commands.

1. create a user alias
## User Aliases
User_Alias ADMINS = jeffin

2. Create a command alias
## Storage
Cmnd_Alias STORAGE = /sbin/fdisk -l

3. Now add those into user privilage specifications,

ADMINS All=STORAGE

if we give like below one, it will through as an error,

jeffin ALL=STORAGE
user/group hostname = (runasuser) command(s)

ERROR: # visudo
visudo: Warning: Runas_Alias `ADMINS' referenced but not defined
visudo: Warning: unused User_Alias ADMINS

insted of the user name give User_Alias name here

Solution: jeffin ALL=STORAGE this is wrong as we mentioned User_Alias above. So re-wright it as,

ADMINS ALL=STORAGE

ADMINS ALL=NOPASSWD:STORAGE  ----------> If we give NOPASSWD we can run our commands without using our password, or else it will ask for your password.

Here ADMINS and STORAGE are user alias

4. From user side you can check for what all commands you have the permission to run. First switch as that user and use the below command,

$ sudo -l
User jeffin may run the following commands on this host:
    (root) /etc/init.d/httpd reload
    (root) /sbin/fdisk -l

5. switch as that user and run that command ( fdisk -l) to show the result. make sure you run this with sudo

# sudo /sbin/fdisk -l

Disk /dev/sda: 33.3 GB, 33285996544 bytes
255 heads, 63 sectors/track, 4046 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x000ac2fa

   Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *           1          39      307200   83  Linux
Partition 1 does not end on cylinder boundary.
/dev/sda2              39         557     4161536   82  Linux swap / Solaris
Partition 2 does not end on cylinder boundary.
/dev/sda3             557        4047    28036096   83  Linux


Remember this:
• The runasuser token is optional and defaults to root if not included.

• Groups are specified in sudoers file by prefixing the group name with %.

• There can be multiple usernames in a line separated by commas.

• Multiple commands should also be separated by commas. Spaces are considered a part of the command.

• The name of alias should be in capital letters; otherwise it would give a syntax error. 

• If the space in a line gets over, we can put a back slash (\) and continue on the next line.

• While running a sudo command, the user will be prompted for its own password, not the password of the user it is trying to act as.

 Giving privileges for a group

1. Create a group jeep

# groupadd jeep

2. Create and add users into that group

# useradd -g jeep jeff
# useradd -g jeep jomy

3. ## Allows people in group jeep to run commands specified in the cmd-alias STORAGE

%jeep ALL=STORAGE

Here STORAGE is user alias

4. Switch as that user and run

# sudo -l
# sudo /sbin/fdisk -l


 How to gain root privileges for a normal user

By using su - command, a user can login as root after entering root’s password.

But by specifying root privileges for a user in sudoers, it doesn't need to know root password to login as root for that session.

 -- 'su' Substitute User

# su -
# su - root
$ su - root -c "ls -l /root"

 To use a privilege of another user

# sudo -u <user to run command as> <command>

If you want to give privilages for another user give that user name in ()

jeffin ALL=(jeff) /etc/init.d/httpd reload

That says that user jeffin can (using "sudo -u ") run commands as jeff.

[jeffin@localhost ~]$ sudo -u jeff /etc/init.d/httpd reload


 LogFile

By default, sudo messages are sent to syslog.

so all commands run as sudo are logged in /var/log/messages. We can create a separate sudo log file by entering the below line in sudoers file:

# visudo

#Specify default log file location

Defaults logfile=/var/log/sudolog

 Granting Access for user and group together

#granting all access to specific users and groups, separated by commas,

jeffin,%jeep ALL=/etc/init.d/httpd reload

OR

jeffin,%jeep ALL=STORAGE

(STORAGE=/etc/init.d/httpd reload)

Make sure group %jeep is mentioned only in one line. Either where we mentioned group or user.

 Granting access to users for specific files or Dir

This following entry allows user jeff to gain access to all the program files in the /sbin and /usr/sbin directories, 
along with the privilege of running the command /usr/local/src/script.sh:

jeff ALL= /sbin/, /usr/sbin, /user/local/apache/bin/run.sh 

Sunday, 27 April 2014

Shell - For Loop

A 'for loop' is a bash programming language statement which allows code to be repeatedly executed.  For example, you can run UNIX command or task 5 times or read and process list of files using a for loop.

Bash by default support three types of loops 
for loop, while loopuntil loop.

for variable in [list]; do 
          action item
 done

 OR

for variable in [list]
do
  action item
done

argument ---- any variable that we set

argument is equal to the first element or each element in the list as it cycles through the list. It assigns value to the argument.

Exam:

1. 
for contries in INDIA JAPAN MALI
   do
       echo $contries
   done

2.
for VARIABLE in 1 2 3 4 5 .. N
do
command1
command2
commandN
done

OR

3.
for VARIABLE in file1 file2 file3
do
command1 on $VARIABLE
command2
commandN
done

OR

4.
for OUTPUT in $(Linux-Or-Unix-Command-Here)
do
command1 on $OUTPUT
command2 on $OUTPUT
commandN

done

For printing numbers from 1 to 100

Latest bash version 3.0+ has inbuilt support for setting up ranges.  The following example illustrates the use of "ranges" in a for-loop.

for i in {1..10}
do
echo "$i"

done

The expression "{1..10}" is a shorthand for the list "1 2 3 4 5 6 7 8 9 10". 


Bash v4.0+ has inbuilt support for setting up a step value using {START..END..INCREMENT} 


#!/bin/bash
echo "Bash version ${BASH_VERSION}..."
for i in {0..10..2}
  do
     echo "Welcome $i times"
 done
Following is the example to display all the files starting with .bash and available in your home.

#!/bin/sh

for FILE in $HOME/.bash*
do
   echo $FILE
done
This will produce following result:
/root/.bash_history
/root/.bash_logout
/root/.bash_profile
/root/.bashrc

The for statement is used when you want to loop through a list of items. The body of the loop is put between do and done. Let's say that we want to write a program that will validate numbers in a given list. These numbers can be loaded from a file, hard coded, or manually entered by the user. For our example, we will ask the user for a list of numbers separated with spaces. We will validate each number and make sure that it is between 1 and 100. The best way to write a program like this would be to use a for loop. 

#!/bin/sh
# Validate numbers...

echo "Please enter a list of numbers between 1 and 100. "
read NUMBERS

for NUM in $NUMBERS
do
 if [ "$NUM" -lt 1 ] || [ "$NUM" -gt 100 ]; then
  echo "Invalid Number ($NUM) - Must be between 1 and 100!"
 else
  echo "$NUM is valid."
 fi
done

In some cases it is more suitable to use a while-loop instead of a for-loop. The following example does essentially the same the for-loop above:


#!/bin/bash
count=1
while [[ $count -le 9 ]]
do
    echo "$count"
    (( count++ ))
done

-------------------------------------------------------

[carol@octarine ~/html] cat html2php.sh

#!/bin/bash
# specific conversion script for my html files to php
LIST="$(ls *.html)"
for i in "$LIST"; do
     NEWNAME=$(ls "$i" | sed -e 's/html/php/')
     cat beginfile > "$NEWNAME"
     cat "$i" | sed -e '1,25d' | tac | sed -e '1,21d'| tac >> "$NEWNAME"
     cat endfile >> "$NEWNAME"
done

---------------------------------------------------------------

 Using Arguments

Example 10. Shell Script Arguments

#!/bin/bash
# example of using arguments to a script
echo "My first name is $1"
echo "My surname is $2"
echo "Total number of arguments is $#" 

----------------- ---------------- --------------- -------------
echo "$0 counts the lines of code" 
l=0
n=0
s=0
for f in $*
do
l=`wc -l $f | sed 's/^\([0-9]*\).*$/\1/'`
echo "$f: $l"
        n=$[ $n + 1 ]
        s=$[ $s + $l ]
done

   Index Of Commands

--> for a in $@ Every input

--> $1, $2, ... First input, second input, ... $0 Name of script (command you typed to run it.) $# Number of inputs

--> $@ All inputs (1-end) with a space between $? Return value of last command (tricky to use.)

--> # Comment. Must be first thing in line

--> $abc Look up value of abc variable

--> $(abc) Run command "abc". $($abc) looks up abc and runs it.

--> ${abc} {}'s are optional parenthesis. They are needed for parms past 9 (${10}).

--> [ ] Short cut for test

--> ${1:3} Substring of $1, starting at 3rd position. ${abc:2:4} Substring of abc, starting at 2nd position and using next 4 characters.


--> "read a" Read one line from keyboard and put in variable a

--> 'abc' (quotes near ENTER): Treat this as a bunch of letters.

--> "abc" (double quotes): Expend all $-rules, but otherwise treat as a bunch of letters.

--> `abc` (back-tic [upper-left]): same as $(abc). Runs command. eval abc: Evaluate. Also same as $(abc).

-------------------- -------------------       ---------------------------------

for a in 4 8 3 7 13 4
do
  echo -n $a"  "
  if test $a -ge 5
  then
    echo "big"
  else
    echo "small"
  fi
done

---------------------------------------- ------------------------------------- ------------------------------

if [ $# -ne 1 -o ! -f $1 ]
then
  echo "Usage: $0 FILENAME"
  exit 1
fi

words=0
for a in $(cat $1)
do
#  echo "["$a"]"
words=$((1+$words))
done

echo "Num of words is" $words

$(cat $1) simply "pastes" that file after for a in, which then looks at every word. 
Note the output of cat is automatically redirected (it does not go to the screen.) words is a standard counter, with one added for each word, in the awkward script manner. If you have a lot of math, use a more formal programming language.

The first part is a standard check: if there is not one input, or it is not a file (-o (letter O) is or, ! is not -- see %info test).
------------------------------------- ------------------------------- ----------------

  Three-expression bash for loops syntax

It is characterized by a three-parameter loop control expression; 
consisting of an initializer (EXP1), a loop-test or condition (EXP2), and a counting expression (EXP3). 

for (( EXP1; EXP2; EXP3 ))
do
command1
command2
command3
done

#!/bin/bash
for (( c=1; c<=5; c++ ))
do
   echo "Welcome $c times"
done

-------------  ----------------------- --------------

$ cat for4.sh
i=1
for username in `awk -F: '{print $1}' /etc/passwd`
do
 echo "Username $((i++)) : $username"
done

$ ./for4.sh
Username 1 : ramesh
Username 2 : john
Username 3 : preeti
Username 4 : jason
----------------------------- --------------------------- ----------- 

  Loop through files and directories in a for loop

To loop through files and directories under a specific directory, just cd to that directory, and give * in the for loop as shown below.

The following example will loop through all the files and directories under your home directory.

$ cat for5.sh
i=1
cd ~
for item in *
do
 echo "Item $((i++)) : $item"
done

$ ./for5.sh
Item 1 : positional-parameters.sh
Item 2 : backup.sh
Item 3 : emp-report.awk
Item 4 : item-list.sed
Item 5 : employee.db
Item 8 : storage
Item 9 : downloads
Usage of * in the bash for loop is similar to the file globbing that we use in the linux command line when we use ls command (and other commands).

For example, the following will display all the files and directories under your home directory. This is the concept that is used in the above for5.sh example.

cd ~
ls *