d2jsp
Log InRegister
d2jsp Forums > Off-Topic > Computers & IT > Programming & Development > Make Me A Better Programmer - From Step 1
Prev1293031323356Next
Add Reply New Topic New Poll
Member
Posts: 23,862
Joined: Aug 16 2006
Gold: 20.00
Feb 9 2014 10:33pm
well, I got a working product. Doesn't format as pretty as I want, but I was more concerned with finishing the major requirements.

Code style still needs some work, several examples of frustration can be seen (repeated code in a place or two)

Two files are included - my main.c and the helper file (mostly just text printing)

Code
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <libgen.h>
#include "queue.h"
#include "helpers.h"

/*
$Author: eep (har har) $
$Date: 2014/02/10 03:52:47 $
$Log: bt.c,v $
Revision 1.8 2014/02/10 03:52:47
Fixed a large bug with the queue and how the loops interacted.

Revision 1.7 2014/02/10 00:14:46
Moved all functions to a helpers file, included in the make utility.
Added suffix rules to make utility.

Revision 1.6 2014/02/08 23:07:01
Now correctly displays depth levels. Added -t option. Fixed syntax on error printing.
Removed some unused variables, patched possible memory leak.

Revision 1.5 2014/02/06 21:09:11
Refactored some parts and modularized the option printing into a function

Revision 1.4 2014/02/06 19:27:47
Added comments, modified some more formatting.

Revision 1.3 2014/02/05 20:26:18
Added more options, tried to fix some issues with indenting

Revision 1.2 2014/02/04 23:31:49
Added basic traversal system.

*/

int main(int argc, char *argv[]) {

/* DATA DECLARATIONS */

int options[10] = { 0 };
int I_OPT = 4;
int opt = 0;
char *path;
char *name;
char *initialPath;
struct queue *q = NULL;
struct stat stats;
struct passwd *passwd;
struct group *group;
struct dirent *dent;
DIR *dir;
long pathLen, tempLen;
int x;
int init = 0;
int currentDepth = 1;
int nodes = 0;

/* END DATA DECLARATIONS */

while ((opt = getopt (argc, argv, "hLdgipstuI:l")) != -1) {
switch (opt) {
case 'h':
helpResponse();
return 0;
break;
case 'L':
options[0] = 1;
break;
case 'd':
options[1] = 1;
break;
case 'g':
options[2] = 1;
break;
case 'i':
options[3] = 1;
break;
case 'p':
options[4] = 1;
break;
case 's':
options[5] = 1;
break;
case 't':
options[6] = 1;
break;
case 'u':
options[7] = 1;
break;
case 'I':
options[8] = 1;
I_OPT = atoi(optarg);
break;
case 'l':
options[9] = 1;
break;
}
}

/* This checks for user path, else sets cwd as path */

if (argc > optind) {
path = argv[argc-1];
} else {
path = getcwd(NULL, 64);
}

/* Enqueue the initial path (from user arg or cwd) */

enqueue(&q, path, currentDepth);
nodes++;

/* Begin outer loop - loops until queue is empty..*/

while (!isEmpty(q)) {
if ((dir = opendir(front(q))) == NULL) {
perror("bt: Directory Opening Error");
printf("Path in question: %s\n", front(q));
return 1; //exit program to prevent infinite loops
} else {

/* Get path name from top queue node, dequeue top node, begin directory reading while-loop */

pathLen = strlen(front(q));
name = front(q);
currentDepth = getDepth(q);
dequeue(&q);
nodes--;
while ((dent = readdir(dir)) != NULL) {

/* Get rid of '.' and '..' as possible results */

if ((strcmp(dent->d_name, "..") != 0) && (strcmp(dent->d_name, ".") != 0)) {

/* Correctly print out the initial filename(starting path base name) */

if (!init) {
stat(path, &stats);
initialPath = basename(path);
printf("%-25s", initialPath);
printOptions(stats, options, passwd, group);
init = 1;
}

/* Get length of path name and file name to malloc correct size of new string */

tempLen = strlen(dent->d_name);
path = (char *)malloc(tempLen + pathLen + 2);
strcpy(path, name);
strcat(path, "/");
strcat(path, dent->d_name);

/* Stat the current file, if there is an issue we report it */

if (options[0]) {
if(lstat(path, &stats) < 0) {
perror("\n\t\t\t\t\t\tbt: File Stat Error");
printf("\t\t\t\t\t\tProblematic path: %s\n\n", path);
} else {
/* Is the file a directory? If so, add it to the queue! */

if (S_ISDIR(stats.st_mode)) {
enqueue(&q, path, currentDepth+1);
nodes++;
}

/* Indents sub directories */

for (x = 0; x < I_OPT * currentDepth; x++) {
printf(" ");
}

/* Print information on files based on what options are flagged */

printf("%-25s", dent->d_name); //Poor attempt at formatting text

printOptions(stats, options, passwd, group);

}
} else {
if (stat(path, &stats) < 0) {
perror("\n\t\t\t\t\t\tbt: File Stat Error");
printf("\t\t\t\t\t\tProblematic path: %s\n\n", path);
} else {

/* Is the file a directory? If so, add it to the queue! */

if (S_ISDIR(stats.st_mode)) {
enqueue(&q, path, currentDepth+1);
nodes++;
}

/* Indents sub directories */

for (x = 0; x < I_OPT * currentDepth; x++) {
printf(" ");
}

/* Print information on files based on what options are flagged */

printf("%-25s", dent->d_name); //Poor attempt at formatting text

printOptions(stats, options, passwd, group);

}
}
}
} //inner loop
closedir(dir);
//free(path);
} // if-else
} //outer loop
free(path);
return 0;
}

/* HELPERS.C */

void helpResponse() {
printf("Correct usage: bt [-h] [-L -d -g -i -p -s -t -u -In -l] [dirName]\n");
printf("dirName is optional, by default bt uses the current working directory\n");
printf("Options: -h lists help, -L follows symbolic links, -d shows last modification...\n");
printf("... -g prints GID, -i prints # of links to file, -p prints perms, -s prints size of file in bytes...\n");
printf("... -t prints info on file type, -u prints UID, -I [n] sets depth indendation by n spaces...\n");
printf("... -l runs bt as though the flags -t -p -i -u -g -s are set.\n");
};


void printOptions(struct stat stats, int *options, struct passwd *passwd, struct group *group) {
long fileSize = 0;
char *timeFetch;

printf("\t\t\t");

if (options[6]) {
printFileType(stats);
}

/* This code uses a bit-wise AND with the value of st_mode + pre-defined values of various permissions to output correct perms */

if (options[4] || options[9]) {
printf((S_ISDIR(stats.st_mode)) ? "d" : "-");
printf((stats.st_mode & S_IRUSR) ? "r" : "-");
printf((stats.st_mode & S_IWUSR) ? "w" : "-");
printf((stats.st_mode & S_IXUSR) ? "x" : "-");
printf((stats.st_mode & S_IRGRP) ? "r" : "-");
printf((stats.st_mode & S_IWGRP) ? "w" : "-");
printf((stats.st_mode & S_IXGRP) ? "x" : "-");
printf((stats.st_mode & S_IROTH) ? "r" : "-");
printf((stats.st_mode & S_IWOTH) ? "w" : "-");
printf((stats.st_mode & S_IXOTH) ? "x" : "-");
}

if (options[3] || options[9]) {
printf("\t%ld\t", stats.st_nlink);
}

if (options[7] || options[9]) {
passwd = getpwuid(stats.st_uid);
printf( "%s", passwd->pw_name);
}

if (options[2] || options[9]) {
group = getgrgid(stats.st_gid);
printf("\t%s", group->gr_name);

if (options[5] || options[9]) {
fileSize = stats.st_size;
if (fileSize < 1024) {
printf("\t%ld", fileSize);
} else if (fileSize >= 1024 && fileSize < 1048576) {
printf("\t%ldK", fileSize / 1024 );
} else if (fileSize >= 1048576 && fileSize < 1073741824) {
printf("\t%ldM", fileSize / 1048576);
} else {
printf("\t%ldG", fileSize / 1073741824);
}
}

/* Print last date modified - ctime returns string with a newline so don't double print newlines */

if (options[1] || options[9]) {
timeFetch = ctime(&stats.st_mtime);
printf("\t%s", timeFetch);
} else {
printf("\n");
}
};

void printFileType(struct stat stats) {
switch (stats.st_mode & S_IFMT) {
case S_IFBLK:
printf("block");
break;
case S_IFCHR:
printf("character device");
break;
case S_IFDIR:
printf("directory");
break;
case S_IFIFO:
printf("pipe");
break;
case S_IFLNK:
printf("symbolic link");
break;
case S_IFREG:
printf("regular file");
break;
case S_IFSOCK:
printf("socket");
break;
default:
break;
}
printf("\t");
};
Member
Posts: 1,995
Joined: Jun 28 2006
Gold: 7.41
Feb 9 2014 11:12pm
You are probably going to want to get out of the habit of making line comments with /* */ because if you ever want to comment out a block of code you are going to run into issues. Use // for single line comments
Member
Posts: 23,862
Joined: Aug 16 2006
Gold: 20.00
Feb 9 2014 11:22pm
Quote (Minkomonster @ Feb 10 2014 12:12am)
You are probably going to want to get out of the habit of making line comments with /* */ because if you ever want to comment out a block of code you are going to run into issues. Use // for single line comments


good point, it was my first time really commenting it because the professor asked specifically. I'll keep that in mind.
Member
Posts: 3,580
Joined: Aug 17 2013
Gold: 275.01
Feb 10 2014 03:17pm
You can also use
/**
Comments here.
More comments here.
@params here
*/

I believe this, along with Doxygen, generates nice documentation for your code. Could be wrong, but either way you should check it out. Just lookup Doxygen I guess..

Member
Posts: 23,862
Joined: Aug 16 2006
Gold: 20.00
Feb 13 2014 01:44am
This weekend is gonna be fun

Code
Concurrent UNIX Processes and shared memory
The goal of this homework is to become familiar with concurrent processing in the Unix operating system. You
will write a program that uses multiple processes to compute the product of two matrices Anm and Bmk. Each
element of the resultant matrix C is given by

There are two types of processes for this homework:

 A \coordinator" process: It is responsible for creating the \worker" processes, and coordinating the computa-
tion. Note that all the computation is done by the \worker" processes. All the information is provided in the
command line (argv).

 A set of \worker" processes: Each worker process reads two integer indices i and j from its argv, computes
the result using expression 1 and returns the result into shared memory before exiting. So, a worker process
is created for every element of the resultant matrix C.

The coordinator process also set a timer at the start of computation. If computation has not nished by this time,
the coordinator process kills all the workers and then exits. Make sure that you print appropriate message(s).

The code for worker process should be compiled separately and its executable be called worker. The executable for
the coordinator process should be called coord. The two matrices to be multiplied should be kept in separate les
whose names are to be provided as command line arguments. Thus the program should be executed by

coord -t time A.mat B.mat C.mat

where time is the duration after which the program will be killed (default value: 100).
The format of each matrix le with n rows and m columns is as follows:
n m
a11 a12 a13    a1m
a21 a22 a23    a2m
a31 a32 a33    a3m
...
...
...
...
...
an1 an2 an3    anm

Each worker process prints its process id, its operands and their sum. Each time coord gets a message back from a
worker, it prints the pid of the worker and the result received into the log le.

Implementation

The coordinator will create the matrices in the shared memory after reading them from the files, and write the result
into a third file after all the children have finished.

The code for coordinator and worker processes should be compiled separately and the executables be called coord
and worker.

coord will just spawn the worker processes and wait for them to nish. Make sure that you never have more than
twenty processes at any time in the system, no matter how big the matrix. coord also sets a timer at the start of
computation to a speci ed number of seconds (default 100). If computation has not nished by this time, coord kills
all the worker processes and then exits. Make sure that you print appropriate message(s).

In addition, coord should print a message when an interrupt signal (^C) is received. Other signals are ignored. The
child processes will be killed when the interrupt is received by coord. Then, coord should be killed as well. You can
kill the children explicitly from coord, or by sending a signal to self. Make sure that the processes handle multiple
interrupts correctly. As a precaution, add this feature only after your program is well debugged.

coord will also allocate shared memory for synchronization purposes (flag and turn). Each child will write into a
log le created by coord. The information to go in the log le will be the point when the worker attempts to enter
the critical section, and when it exits the critical section. You should keep track of the number of times worker
attempted to enter critical section before it succeeded and write that information in the log le. Writing into the
log le will be performed from inside the critical section. Also add a sleep function in the critical section to sleep for
a random amount of time between 0 and 3 seconds.

The code for main and child processes should be compiled separately and the executables be called coord and worker.
Hints
You will need to set up shared memory in this project to allow the processes to communicate with each other. Please
check the man pages for shmget, shmctl, shmat, and shmdt to work with shared memory.

You will also need to set up signal processing and to do that, you will check on the functions for signal and abort.
If you abort a process, make sure that the parent cleans up any allocated shared memory before dying.
In case you face problems, please use the shell commands ipcs to nd out any shared memory allocated to you and
free it by using ipcrm.


not allowed to use bakery algorithm :[



This post was edited by Eep on Feb 13 2014 01:44am
Member
Posts: 11,610
Joined: Oct 28 2008
Gold: 1,795.00
Feb 13 2014 01:55am
Quote (Eep @ Feb 13 2014 01:44am)
This weekend is gonna be fun

not allowed to use bakery algorithm :[


You have to do threading with just bash?

oh dear, good luck
Member
Posts: 23,862
Joined: Aug 16 2006
Gold: 20.00
Feb 13 2014 02:22am
Quote (0n35 @ Feb 13 2014 02:55am)
You have to do threading with just bash?

oh dear, good luck


oh its in C
Member
Posts: 23,862
Joined: Aug 16 2006
Gold: 20.00
Feb 17 2014 09:38pm
got my shared memory set up, doing it in 4 segments though currently (one for each matrix and one for a struct).

need to get my worker processes to do the matrix mult and then I can start working on having them communicate nicely w/o race conditions and writing to the log file

This post was edited by Eep on Feb 17 2014 09:38pm
Member
Posts: 23,862
Joined: Aug 16 2006
Gold: 20.00
Feb 20 2014 10:38pm
mostly done with the assignment, just need to trap some signals and stuff
Member
Posts: 23,862
Joined: Aug 16 2006
Gold: 20.00
Feb 24 2014 10:02pm
who is ready for another ugly code dump in a bit


I know my professor isn't


tldr explanation of program:

program is basically a complicated matrix multiplication solver using concurrent processes and shared memory.

coord sets up a bunch of shared memory segments for arrays and stuff.

then spawns children and execs them right away into the worker programs (passing the row, col to do multiplication on to the child)

it waits for them to finish, then writes their results to a log file

the worker has a function process() for handling race conditions.

it gets access to the shared memory (IPC), solves the multiplication, writes the element to the shared result matrix, then enters the process() function to get access to critical section

in the critical section, a worker just writes a bunch of information to a log file (created by coord)

This post was edited by Eep on Feb 24 2014 10:30pm
Go Back To Programming & Development Topic List
Prev1293031323356Next
Add Reply New Topic New Poll