Skip to content

Instantly share code, notes, and snippets.

@syall
Last active January 31, 2024 02:50
Show Gist options
  • Save syall/a0765067f5070216819ba2713f1cc2b2 to your computer and use it in GitHub Desktop.
Save syall/a0765067f5070216819ba2713f1cc2b2 to your computer and use it in GitHub Desktop.
Rutgers Computer Science iLab Modern Environment Walkthrough

RU Computer Science iLab Environment Walkthrough

Overview

Rutgers University Computer Science Students have to use iLab machines for courses such as Systems Programming and Computer Architecture, but there is usually no comprehensive set up tutorial.

Because of this, many of the modern amenities that exist are not utilized, and students are stuck in a terminal (not user-friendly).

In this guide, there will be examples of how to ssh into an iLab machine from a terminal, set up boilerplate code for C Projects, configure Visual Studio Code (VS Code) for remote development, and work with git and GitHub both locally and remotely.

Side note:

Only intended for MacOS and Linux users, although there are equivalents to all of these steps on Windows.

Also, this guide may not produce the best result for every reader, but is just a guide for one developer environment that some students found to be productive.

Table of Contents

  1. Simple ssh into an iLab Machine
  2. C Project Structure
    1. Makefile
    2. C File
    3. C Output
  3. Setting up Visual Studio Code
  4. Overview and Configuration of Git and GitHub

Simple ssh into an iLab Machine

To ssh into an iLab Machine, students need to first create a Computer Science Department Systems account, which is different from the regular Rutgers netid account.

To activate an account, follow the steps here.

Also, this website is also where the account password can be changed as well as other authentication configurations.

Once an account has been created, open a Terminal Application and type in the following command:

# Format: ssh <netid>@<name-of-ilab-machine.cs.rutgers.edu>
# Example:
ssh ab123@cheese.cs.rutgers.edu

If successful, the terminal will prompt you for the password to the activated account (password input will not show up in the terminal).

Input the password into the terminal and run ls to see what folders are there by default.

Congratulations! Access to editing, adding, and deleting files and directories is now possible!

Logging into an iLab is all that is necessary to begin work on these machines, but this guide will go into more features to simplify your workflow.

C Project Structure

In classes such as Systems Programming and Computer Architecture, iLab machines are used to have consistent compilation and testing for assignments because gcc is used to compile these assignments, which means depending on what CPU architecture the code is compiled on, the output may not be compatible on every machine!

The three files below will represent a working boilerplate example of what could be useful when scaling out projects for these classes.

Makefile

The Makefile is a definition for certain directives which usually run other directives or commands, usually for compiling, running, testing, and cleaning.

Running a Makefile

A Makefile is "ran" by using the command-line tool make, in which you can supply arguments to run specific directives as well.

# Running make with no arguments, meaning it will default to "all"
make
# Running with an argument, meaning it will run that specific directive
make file
make clean

Example: Makefile

# all directive: make with no arguments runs the all directive
all: file # Anything after a colon in a directive is a dependency
         # make will check if those directives, files, directories
         # run or exist respectively
file: file.c # a directive named file that requires file.c
    # gcc compiles with warning flags, input file.c, and output file name
    gcc -Wall -Werror -fsanitize=address file.c -o file.out
clean: # clean directive is best practice for Makefiles: rm executables
    rm *.out # remove files that have a file extension .out

C File

The C programming language is usually described as a low level language due to the aspect of memory management as well as working with more complicated API functions that usually tie closely with *nix architecture.

However, this example is pretty simple in that only the most basic constructs are used for a working example.

In later lectures and projects, topics such as sockets and memory management increase the complexity, but for boilerplate code such as this, there is no need for a deep dive into the language.

Side Note: 2 main features that stereotype the language are pointers and memory management.

Example: file.c

file.c is a simple program that returns whether an input is an even or odd number.

The comments are inline with the code to explain how the code works, but there is also a code block below with no comments.

This file is compiled into file.out with gcc, turning this higher-level human-readable C code into machine code that is not worth comprehending for a student.

With Comments
#include <stdio.h>
#include <stdlib.h>
/*
^^^
Header Files:
stdio.h and stdlib.h mean standard IO and standard library,
in which many useful functions that are necessary even for basic
programs, such as this one, exist.
In most cases, including these two header files is the safest option.
*/

/*
main function:
When executing a file, the main function is what is "run", including two
parameters argc and **argv.
argc refers to the number of arguments there are, including the
file path itself.
argv is a pointer to a set of char*, usually referred to as
strings, which hold the actual values of the command line execution.

Example: "./file.out 13" has argc = 2, argv = ["./file.out", "13"]
*/
int main(int argc, char **argv)
{
    /*
    One test case that should be covered is whether the
    number of arguments is correct.
    In this case, the if statement is checking whether argc
    is not equal to 2, meaning that there is only the file
    path or there is more than one additional argument supplied.
    */
    if (argc != 2)
    {
        /*
        printf function:
        The printf function is useful for formatting outputs.
        In this case, it simply prints out a string to the terminal.
        Notice that there is a control character "\n" at the end.
        This is necessary if a new line is desired, as printf will
        not start on a new line unless explicitly written.
        In Java, this is built-in with System.out.println(), but can
        also have the new line omitted with System.out.print().
        Another use case is having string formatting in which you take
        more arguments and specify its place in the format string.

        Example:
        printf("%s invalid number of inputs %d\n", "There is an", 3);
        The output would be "There is an invalid number of inputs 3\n".
        The second argument would substitute into %s and the 3 would
        substitute %d.
        */
        printf("This is not a valid input.\n");
        /*
        The return value of the main function typically
        denotes the result of executing a program.
        The most common codes are 0 for success and 1 for error.
        Since entering this if block means that there is an error, the
        return value is 1.
        For more complex errors, other numbers can be used to enumerate
        different errors.
        */
        return 1;
    }

    /*
    atoi function:
    Here we see the first case of C types.
    Since our argument is a string, or more specifically a char*,
    we cannot perform mathematical operations on it!
    Instead, we need to convert the string to a number.
    The atoi function, though generally unsafe, converts the string
    to an int and is stored in a variable p of type int.
    */
    int p = atoi(argv[1]);

    /*
    Another if block but followed with an else block.
    In this case, if p % 2 has no remainder, that means
    the number p is an even number, so "even\n" is printed.
    If it is any other case, then "odd\n" is printed instead.
    */
    if (p % 2 == 0)
    {
        printf("even\n");
    }
    else
    {
        printf("odd\n");
    }

    // Success! Return 0 as the success code.
    return 0;
}
Without Comments
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
    if (argc != 2)
    {
        printf("This is not a valid input.\n");
        return 0;
    }

    int p = atoi(argv[1]);

    if (p % 2 == 0)
    {
        printf("even\n");
    }
    else
    {
        printf("odd\n");
    }

    return 0;
}

C Output

The compiled output is not human readable, essentially gibberish.

However, to a computer, it is the executable code for that particular iLab machine!

There is no guarantee that the code generated will work on other operating systems or even different machines.

Running file.out

After running make, to execute file.out run this command:

# Format <path>/<to>/file,out <...arguments>
# Example: If file.out is in the current directory
./file.out 13

Example: file.out

Here is a snippet of file.out, as the entire file looks like this.

�ELF����>��@@()@8    @%$��@@@@@�������8�8�@8�@�����@@�    �     ���
�
`�
`���� ���
�
`�
`�����T�T�@T�@DD�P�td�P�P�@P�@DD�Q�td��R�td��
�
...

Setting up Visual Studio Code

Visual Studio Code (VS Code) is an Integrated Developer Environment (IDE), which simplifies down to a program which helps developer productivity by automating editing, compiling, testing, etc.

VS Code is lightweight and fast, as well as supporting many thousands of extensions for dozens of programming languages, many with first class support, making it useful not only for C development but also for any generic programming assignment.

However, this guide will only cover how to connect to the iLab machine through VS Code and none of its other features.

To download, visit the website and download the version on your local machine.

Once downloaded, open the application and find the tab on the left toolbar for extensions and search remote development and download the extension pack provided by Microsoft.

Once downloaded, open the new tab on the left sidebar called Remote Explorer.

Under SSH Targets, click the settings cog and edit the custom configuration file.

Once open, write an entry in the file that has this format:

Host <ilab-machine-name>.cs.rutgers.edu
    User <netid>
    HostName <ilab-machine-name>.cs.rutgers.edu
    IdentityFile ~/.ssh/id_rsa
    ControlMaster auto
    ControlPath  ~/.ssh/sockets/%r@%h-%p
    ControlPersist  3600

Once saved, the computer should show up under the SSH Targets bar.

Now in the terminal, run ssh-keygen -t rsa -b 4096 and press enter through all of the prompts.

Then, run ssh-copy-id <netid>@<ilab-machine-name>.cs.rutgers.edu and enter the iLab password when prompted.

Then, run mkdir -p ~/.ssh/sockets.

Finally, return to VS Code and click the add folder icon next to the ssh target entry and enter the password for the iLab machine.

Once successful, open the explorer tab on the left navigation bar of the VS Code window and click Open Folder and choose any folder to open.

From here, VS Code can now edit, add, and delete any existing files and directories on an iLab machine from a local VS Code editor!

Opening up the VS Code integrated terminal will also allow terminal commands to be run on the iLab machine (such as make) making it useful for having all of the tools to build a project all in one window.

Working with a modern editor is much more user friendly than using a terminal editor as all of the extensions from VS Code are also at the user's disposal and can make working on any assignment much more productive!

For more tips and tricks using VS Code Remote Development, use this link from Microsoft as a guide.

Overview and Configuration of Git and GitHub

Git is a command line utility that provides version/source control.

Version Control is the history of what was written and by who at a single point in time, represented by branches as well as checkpoints called commits which is useful when a user wants to find changes, breaking or not.

However, I will not be discussing branches as that has its only developer complexity with many different workflows, although they all accomplish the same thing.

In a sense, the git cycle of development can be summarized in 4 steps:

  • Make Changes to Code
  • Add those Changes to a Staging Area
  • Commit those Changes to the Branch
  • Push those Commits to a Remote Repository

To first begin, go to the directory in which you want git to be used.

In this example, the test/ directory will be followed with the boilerplate code above and a .gitignore file which is currently empty:

test
├── .gitignore
├── Makefile
└── file.c

0 directories, 3 files

In the command line, cd into the directory and run the command git init (assuming git is already installed on the machine).

Running the command will create a .git/ directory with the record for all of the changes that will be added. However, the .git/ directory should NOT be modified manually at all, as all interactions are usually through the command line tool.

test
├── .git
│   ├── HEAD
│   ├── config
│   ├── description
│   ├── hooks
│   │   ├── applypatch-msg.sample
│   │   ├── commit-msg.sample
│   │   ├── fsmonitor-watchman.sample
│   │   ├── post-update.sample
│   │   ├── pre-applypatch.sample
│   │   ├── pre-commit.sample
│   │   ├── pre-merge-commit.sample
│   │   ├── pre-push.sample
│   │   ├── pre-rebase.sample
│   │   ├── pre-receive.sample
│   │   ├── prepare-commit-msg.sample
│   │   └── update.sample
│   ├── info
│   │   └── exclude
│   ├── objects
│   │   ├── info
│   │   └── pack
│   └── refs
│       ├── heads
│       └── tags
├── .gitignore
├── Makefile
└── file.c

9 directories, 19 files

To see what the status of the newly converted git repository is, run git status.

The output should mention that .gitignore, Makefile, and file.c are untracked, which is true since we never had any record of the files before in git.

To add the files, run git add ..

This will add the current directory, which is a shortcut to add all changes.

However, you can also add individual files or folders by running git add <path> if there were specific files or folders that needed to be added.

Now looking at the status, the files are in the Staging Area, where changes to be committed are stored.

A commit will officially add a checkpoint to a timeline, meaning there will be an official record of what was changed in the git log.

To make the first commit, run git commit -m 'First commit!'.

The -m flag stands for message, which is what 'First commit!' is!

Having descriptive messages for commits will help people keep track of what changes were made in that commit, making it easier to follow the path of progress.

The final step is to push the commits somewhere, usually to a remote repository.

Instead of keeping all of the changes on the computer which you are working on, there can be a single source of truth in which everyone working on the project can refer to.

However, we have no remote repository set up!

In this case, we will use GitHub, a free git hosting platform.

Create an account or Log in with an existing one and create a new repository with whatever name wanted.

However, do not initialize the repository with a README.

From there, one of the options will be …or push an existing repository from the command line.

Copy and Paste the two commands underneath that section and run in the terminal.

Given that there has been no configuration for GitHub credentials on your local machine, there should be a prompt in the terminal for the GitHub username and password.

Once completed, refresh the GitHub webpage with the repository.

Congratulations! If successful, then the code on the local machine should be on website!.

For any future changes, follow the same cycle: change, add, commit, push.

The .gitignore file is used to ignore files that are not meant to be kept in version control, such as executables or files with private information.

# Files can be specified with specific file paths or with patterns
# The * pattern matches at least 1 character as a wildcard and is
# most commonly used!
# Any file that is already tracked will not be affected by any
# changes to the .gitignore.
# Any file that falls under any of these rules defined here will
# be ignored, so make sure the right files are being ignored
# when using more complicated rules!

# no files that end with .out
*.out
# specific no folder that is named docs/build
docs/build
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment