Creating an Installer for PSLab Desktop App

PSLab device is made useful with applications running on two platforms. One is Android and the other one is a desktop application developed using Python frameworks. Desktop application uses half a dozen of dependent libraries and they are required to be installed prior to installing the application itself.

For someone with zero or less knowledge on how to install packages in a Linux environment, this task will be quite difficult. To ease up the process of installing the desktop application in a computer, we can use a script to run specific commands which will install the dependencies and the application.

Dependencies required by PSLab  Desktop app

  • PyQt 4.7
  • Python 2.6, 2.7 or 3.x
  • NumPy, Scipy
  • pyqt4-dev-tools
  • Pyqtgraph
  • pyopengl and qt-opengl
  • iPython-qtconsole

These dependencies can be made installed using a bash script running with root permission. A bash script will have the file extension “.sh” and a header line;

#!/bin/bash

A bash script needs to be made executable by the user himself. To do this, user needs to type a one line command in the terminal as follows and enter his password;

sudo chmod +x <Name_of_the_script>.sh

The keyword “sudo” interprets as “Super User DO” and the line follows will be executed with root permission. In other words with administrative privileges to modify system settings such as copying content to system folders.

The keyword “chmod” stands for “Change Mode” which will alter the mode of a file. In current context, the file is made executable by adding the executable property to the bash script using “+x” syntax.

Once the script is made executable, it can be executed using;

sudo ./<Name_of_the_script>.sh

An installer can be made attractive by using different colors rather than the plain old text outputs. For this purpose we can use color syntax in bash script. They are represented using ANSI escape codes and following is a list of commonly used colors;

Black        0;30     Dark Gray     1;30
Red          0;31     Light Red     1;31
Green        0;32     Light Green   1;32
Brown/Orange 0;33     Yellow        1;33
Blue         0;34     Light Blue    1;34
Purple       0;35     Light Purple  1;35
Cyan         0;36     Light Cyan    1;36
Light Gray   0;37     White         1;37

As in any programming language, rather than using the same line in many places, we can define variables in a bash script. The syntax will be the variable name followed by an equal sign with the value. There cannot be spaces around the equal sign or it will generate an error.

GREEN='\033[0;32m'

These variables can be accessed using a special syntax as follows;

${GREEN}

Finally we can output a message to the console using the “echo” command

echo -e "${GREEN}Welcome to PSLab Desktop app installer${NOCOLOR}"

Note that the keyword “-e” is used to enable interpretation of the following backslash escapes.

In order to install the packages and libraries, we use two package management tools. One is “apt” which stands for “Advanced Packaging Tool” and the second is “pip” which is used to download python related packages from “Python Package Index”. The following two lines illustrates how the two commands can be accessed.

apt-get install python-pip python-dev build-essential -y

pip install pyqtgraph

The keyword “-y” avoids the confirmation prompt in console to allow installation by pressing “Y” key every time it installs a package from “apt”.

Resources:

Configuring Document Root Apache2 Ubuntu

In this post I will explain how to setup document root for apache server.

Initially the document root is set to /var/www/html by default. We need to change it when we have web applications in /var/www/html/folder.

I will show an example how to configure document root for our project engelsystem. Engelsystem is downloaded to /var/www/html/ . We must make sure to point our apache2 document root to the Engelsystem directory to prevent any user from accessing anything other than the public/ directory for security reasons.

Changing apache2 document root

The default document root is set in the 000-default.conf file that is under /etc/apache2/sites-available folder.

$ cd /etc/apache2/sites-available
$ sudo nano 000-default.conf

While the file is opened change DocumentRoot /var/www/ with your new folder e.g DocumentRoot /var/www/html/engelsystem/public where your index.php file resides

Set the right Apache configuration

The configuration of the /var/www folder is under /etc/apache2/apache2.conf. Edit this file to add the configuration of your new document root.

$ sudo nano/etc/apache2/apache2.conf

Copy the following:

<Directory /var/www/>
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
</Directory>

and change the directory path:

<Directory /var/www/html/engelsystem/public>
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
</Directory>

Restart Apache

Now we can view the engelsystem at localhost/ or http://[i.p.address]/

$ sudoservice apache2 restart

Allowing web-user on apache server to run scripts as root

Allowing web-user on apache server to run scripts as root

If you are new to this, you might be wondering, what the hell is a web user anyways?

So let’s say that you need a server which hosts a simple web page and does a particular task based on data entered into that web-page.

The normal way of doing this is to navigate to /var/www/html and place the web page you want to host here.

You also need to put your php script in this folder so that it is accessible from the website.
This php script will take in the data from your web-page and run the necessary commands that you need to be executed on the server.( I am assuming you are not using “The Real Dev Language” for now. :p )

I will be using a simple web page and script that I have made for this post.

<html>
<head>
  <title>Apk Generator</title>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
  <link href='https://fonts.googleapis.com/css?family=Roboto:400,100' rel='stylesheet' type='text/css'>
  <link href="css/main.css" rel="stylesheet">
</head>
<body>
<div class="container"><br><br>
<form name="htmlform" id="form" enctype="multipart/form-data" class="col-md-offset-4 col-xs-offset-2 col-xs-8 col-md-4 form-group generator_form" >
  <label for="name">Email</label>
      <input type="email" class="form-control" id="Email" name="Email">
      <br>
      <input type="hidden" id="theme" name="theme" value="light">

      <label for="name">App's Name</label>
      <input type="text" class="form-control" id="App_Name" name="App_Name">
      <br>
      <label> Choose your data source </label>
      <ul style="list-style-type:none">
        <li><input type="radio" name="datasource" value="jsonupload"> Upload your own JSON files </input></li>
        <li><input type="radio" name="datasource" value="eventapi"> API endpoint of event on OpenEvent </input></li>
      </ul>
      <br>
      <section id="eventapi-input" style="display:none;">
        <label for="apiendpoint">Link to Open Event API endpoint</label>
        <input type="url" class="form-control"
        id="Api_Link" name="Api_Link">
      </section>
      <br>
      <section id="jsonupload-input" style="display:none;">
        <input type="file" name="uploadZip" id="uploadZip" class="form-control"/>
        <br>
      </section>
      <br>
      <input type="hidden" name="assetmode" value="download">
      <center>
        <br>
        <div id="status"></div>
        <br>
<tr>
 <td colspan="5" style="text-align:center">
  <button type="submit">Generate and Download app</button>
</td>
</tr>
</table>
</form>
<script src="https://www.gstatic.com/firebasejs/live/3.0/firebase.js"></script>
<script src="https://code.jquery.com/jquery-3.1.0.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.0/jquery-ui.js"></script>
<script>
  $('input:radio[name="datasource"]').change(
  function() {
    if ($(this).is(':checked')) {

      if ($(this).val() === 'mockjson') {
        $('#jsonupload-input').hide(100);
        $('#eventapi-input').hide(100);
      }

      if ($(this).val() === 'jsonupload') {
        $('#jsonupload-input').show(100);
        $('#eventapi-input').hide(100);
      }

      if ($(this).val() === 'eventapi') {
        $('#eventapi-input').show(100);
        $('#jsonupload-input').hide(100);
      }
    }
  });
  var $ = jQuery;
  var timestamp = Number(new Date());
  var form = document.querySelector("form");
      form.addEventListener("submit", function(event) {
        event.preventDefault();
        var ary = $(form).serializeArray();
        var obj = {};
        for (var a = 0; a < ary.length; a++) obj[ary[a].name] = ary[a].value;
          console.log("JSON",obj);
        if(obj.Email == "" || obj.App_Name ==""){
          alert("It seems like you forgot to fill up your email address or the app's name");
          setTimeout("location.reload(true);", 1);
        }
        else{
	alert("Please wait while we generate the app, meanwhile you can stick around to directly download it.The app will also be emailed to you."); 
            $.ajax({
              type: "POST",
              url: "/test.php",
              data: { timestamp : timestamp },
              success: function(response){
                console.log("Success",response);
                window.location = response;
              }
            });
        }
      });
    </script>
    </div>
</body>
</html>

This is basically a web page with some inputText widgets which accept response and send it to a php file named test.php on the server via an AJAX post.

<?php
if(isset($_POST['timestamp']))
{
    $uid = escapeshellcmd($_POST['timestamp']);
    exec("sudo sh /var/www/email.sh $uid");
}
?>

This php script will call a bash script which in turns an email to me with the user’s timestamp as the subject.

Well, here is where the problem arises, as I am trying to run the bash file as root.

You might wonder as to why is this such a big issue?
Why can’t we do that?

Well, we can surely do that on the server but the point to be noted here is that we are not running this script directly from the server.

We are running it from a web page which is hosted on a server.

So our user here is a web user aka www-data rather than being a root user.

The web user is not provided root access by default, but there are ways to get this done.

Solution 1 :

Allow the web user to run only specific scripts as root.

Please note that this is not a ideal workaround.
Ideally your web user should not have root access in any case.
Since that’s cleared up, lets proceed.

This can be done by editing your sudoers list and adding www-data to it.
Open up your terminal and enter the following command.

sudo visudo

Next up, navigate to the end of the file and add the following command there

www-data = (root) NOPASSWD: /path/to/script.sh

In case you have to execute one script as root which in turn executes some more scripts as root, you don’t need to set the path to all of them over here.
Doing it only for the parent script will do the job.

Solution 2 :

Using SuExec

DigitalOcean blog has a very good article on how to execute python scripts as root via the web user through cgi.

You can go through the article here :https://www.digitalocean.com/community/tutorials/how-to-use-suexec-in-apache-to-run-cgi-scripts-on-an-ubuntu-vps

Well, that was all about my findings on properly handling sudo requirements for your web user on your apache server.

I’ll be adding more solutions as I find them along the way.
Meanwhile feel free to comment below your thoughts, suggestions and queries.

Cheers.