Saturday, November 28, 2015

FILE SYSTEM and FUSE

Pagi Semua… Kali ini Aku akan Menjelaskan tentang File System.
Apa itu File System? File System adalah bagian dari sistem operasi yang mengatur file, bagaimana file tersebut dibentuk, diberi nama, diakses, digunakan, dilindungi, diimplementasikan, dan diatur. Ada beberapa tipe file system, beberapa diantaranya adalah:
a.       Disk File System yaitu file system yang menggunakan media fisik seperti disk sebagai media penyimpanan
Contoh: FAT32, NTFS, HFS, ext3, ext4
b.      Flash File system : Merupakan file system yang didesain untuk menyimpan file dalam media penyimpanan berbasis flash memory.
Contoh: JFFS, JFFS2, YAFFS, UBIFS, LogFS
c.      Tape File System atau Linear Tape FS (LTFS) : merupakan file system yang menyimpan file dalam tape atau magnetic tape.
Contoh: IBM LTFS-SDE, HP LTFS
d.      Database File System : Merupakan file system yang menyimpan file sebagai gumpalan dalam sebuah database, bukan dalam hierarki direktori.
Contoh: IBM DB2
e.      Network File System : merupakan file system yang bertindak sebagai client untuk file access protocol yang jauh, menyediakan akses ke file dalam sebuah server.
Contoh: NFS, AFS, SMB protocol
f.       dan lain-lain
Selain File System diatas, ada File System lain yang dibuat oleh system sendiri  yang digunakan untuk mengatur beberapa jenis File System yaitu Virtual File System. Virtual File System (VFS) adalah file system abstrak yang memungkinkan aplikasi client untuk mengakses berbagai file system konkret dengan cara yang sama. Virtual File System berfungsi sebagai pengatur dari semua file system yang sudah di mount.VFS dapat digunakan juga sebagai jembatan atara file system Windows, Mac OS dan Unix. VFS memberikan interface antara kernel dan file system konkret. Virtual File System terdiri dari 3 bagian utama yaitu Superblock, Inode, dan Dentry. Superblock merepresentasikan setiap file system yang telah di mount. inode kependekan dari index-nodes, inode berisi metadata dari file. Inode menunjuk ke superblock dari file system dari sebuah file. Inode berisi data file seperti nomer seri, mode proteksi, ownoer, ukuran, tanggal akses file terakhir, tanggal pembuatan dan modifikasi. Dentry berguna untuk meng-encode struktur tree file system dan nama dari file.
FUSE (Filesystem in Userspace) adalah suatu metode yang dilakukan sistem operasi dimana pengguna yang tidak memiliki akses bisa membuat file system mereka sendiri tanpa harus mengedit kernel. FUSE mengakses dua bagian dari sistem operasi: Userspace dan Kernel. Pada kernel, terdapat Virtual File System dan module FUSE. Keduanya digunakan untuk pengaksesan FUSE. Di bagian Userspace, user mengakses file system. Melalui command yang telah diinterpretasikan oleh glibc, command dikirim ke VFS. Kemudian VFS bergerak ke module FUSE, karena direktori yang diakses merupakan filesystem berbasis FUSE. Kemudian kernel FUSE melalui glibc dan libfuse sebelum akhirnya mengakses filesystem yang dimaksud. Setelah itu, hasil pengaksesan filesystem tersebut dikirim kembali ke kernel FUSE (misalkan, ada filesystem 'myFUSE' yang mengembalikan direktori filesystem yang bersangkutan. Direktori tersebut dikirim ke kernel FUSE). Kemudian kembali ke VFS dan akhirnya kembali ke Userspace lagi.
Kelebihan dari FUSE antara lain:
a.      FUSE tidak bertempat di Kernel, sehingga lebih mudah jika ingin menyebarkan code yang menggunakan FUSE. Karena tidak bertempat di kernel, proses pengetesan dan debugging menjadi lebih mudah dan cepat
b.      Apabila terdapat suatu bug, fix-patch tidak harus menunggu revisi kernel dilakukan. Peng-update-an lebih cepat jika memang dibutuhkan
c.      Kegagalan pada filesystem FUSE tidak berpengaruh pada kernel, karena FUSE tidak bertempat di Kernel
d.      Tidak membutuhkan privilege dari administrator untuk mengakses FUSE, sehingga bisa dijalankan lebih cepat

Template FUSE lengkap bisa dilihat DISINI!

Untuk pengimplementasian FUSE mari kita coba untuk mengubah nama file / ekstensi file ketika file terbuka, memunculkan notifikasi dan memindahkan file tersebut ke suatu folder.File yang akan diimplementasikan adalah file berekstensi .c, .doc, dan .3gp.
Untuk melakukan ini kita membutuhkan 3 fungsi dari FUSE yaitu getatrr, readdirr, dan read.  Fungsi getatrr berguna untuk mendapatkan file yang akan dibuka, tanpa getatrr kita tidak bisa membaca file tersebut. Fungsi readdirr berguna untuk membaca direktori dari file yang akan dibuka. Fungsi read digunakan untuk membaca file dan melakukan sesuatu saat file dibuka, fungsi ini yang nantinya kita ubah untuk menyelesaikan masalah diatas. Untuk fungsi getatrr dan readdirr kita juga ubah sedikit hanya untuk mendapatkan path dari file.

Mari kita mulai !

1.     Buat file c untuk menuliskan kodingan.
2.      Tuliskan include fungsi getatrr sebagai berikut

#define FUSE_USE_VERSION 26

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#ifdef linux
#define _XOPEN_SOURCE 700
#endif

#include <fuse.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <sys/time.h>
#ifdef HAVE_SETXATTR
#include <sys/xattr.h>
#endif

static const char *dirpath = "/home/yozora/Downloads";

static int xmp_getattr(const char *path, struct stat *stbuf)
{
               int res;
               char fpath[1000];
               sprintf(fpath,"%s%s",dirpath,path);
               res = lstat(fpath, stbuf);

               if(res == -1)
               {
                              return -errno;
               }

               return 0;
}
3.       Sesuaikan dirpath di static const char *dirpath = "/home/yozora/Downloads"; dengan path folder yang akan di-mount ke FUSE di komputer anda.
4.      Tuliskan fungsi readdirr sebagai berikut

static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
                                     off_t offset, struct fuse_file_info *fi)
{
               char fpath[1000];
               if(strcmp(path,"/") == 0)
               {
                              path=dirpath;
                              sprintf(fpath,"%s",path);
               }
               else sprintf(fpath, "%s%s",dirpath,path);
              
               DIR *dp;
               struct dirent *de;

               (void) offset;
               (void) fi;

               dp = opendir(fpath);
               if (dp == NULL)
                              return -errno;

               while ((de = readdir(dp)) != NULL) {
                              struct stat st;
                              memset(&st, 0, sizeof(st));
                              st.st_ino = de->d_ino;
                              st.st_mode = de->d_type << 12;
                              if (filler(buf, de->d_name, &st, 0))
                                             break;
               }
                                          
               closedir(dp);
               return 0;
}
5.      Tuliskan fungsi read sebagai berikut

static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
                                  struct fuse_file_info *fi)
{
               int fd;
               int res;
               char fpath[1000];
               sprintf(fpath,"%s%s",dirpath,path);
              
               (void) fi;
              
               char ext[1000];
               int i;
               for(i = 0; i < strlen(fpath) && fpath[i] != '.'; i++);     //mengekstrak ekstensi file
               strcpy(ext, fpath+i);
              
               char cmd[1000];
              
               fd = open(fpath, O_RDONLY);
               if (fd == -1)
                              return -errno;
               else
               {
                              if(strcmp(ext, ".c") == 0 || strcmp(ext, ".3gp") == 0 || strcmp(ext, ".doc") == 0|| strcmp(ext, ".ditandai") == 0)
                              {
                                             system("zenity --width 400 --error --title 'Error' --text 'Terjadi Kesalahan! File berisi konten berbahaya.'");

                                             char from[1000], to[1000], newFolder[1000], filename[1000];
                                            
                                             //mengambil direktori dari file
                                             strcpy(newFolder, fpath);
                                            
                                             //menghapus filename dan menambahkan 'rahasia'
                                             for(i = strlen(newFolder) - 1; newFolder[i] != '/'; newFolder[i--] = 0);
                                             strcat(newFolder, "rahasia");
                                            
                                             //mengambil filename
                                             for(i = strlen(fpath) - 1; fpath[i] != '/'; i--);
                                             strcpy(filename, fpath+(i+1) ); //+1 untuk menghilangkan tanda ‘/’
                                            
                                             //Mengecek ada tidaknya folder rahasia
                                             struct stat s;
                                             if (stat(newFolder, &s) != 0)          //0 berarti folder tersebut tidak ada
                                                            mkdir(newFolder, 0777); //membuat folder 'rahasia' folder ke direktori mountnya
                                            
                                             sprintf(from, "%s", fpath);  //mengambil path file awal
                                             sprintf(to, "%s/%s.ditandai", newFolder, filename);            //membuat path tujuan pemindahan file ke folder ‘rahasia’ dan menambahkan ekstensi ‘.ditandai’
                                            
                                             sprintf(cmd, "mv %s %s", from, to);           //Merename dan memindah file
                                             system(cmd);
                                             return -errno;                                   }
              
                              res = pread(fd, buf, size, offset);
                              if (res == -1)
                                             res = -errno;

                              close(fd);
               }
               return res;
}

6.      Tuliskan fungsi operasi FUSE dan main sebagai berikut

static struct fuse_operations xmp_oper = {
               .getattr = xmp_getattr,
               .readdir = xmp_readdir,
              
               .read                     = xmp_read,
};

int main(int argc, char *argv[])
{
               umask(0);
               return fuse_main(argc, argv, &xmp_oper, NULL);
}

7.      Compile file tersebut dengan menuliskan
gcc -Wall [nama file].c `pkg-config fuse --cflags --libs` -o [nama file]
8.      Buat sebuah direktori untuk tempat tujuan mount fuse, misalnya: /tmp/fuse
9.      Jalankan fuse tadi dengan cara: ./[nama file] /tmp/fuse
10.   Cobalah buka file yang ada (catatan bukalah melalui terminal, jika membuka file langsung, maka saat membuka folder peringatan akan keluar karena saat kita membuka folder maka otomatis file didalamnya akan terbaca langsung) 
11.   SELESAI!

Sekian Pembahasan File System dan FUSE semoga bermanfaat! Sampai jumpa lagi!


Template FUSE Lengkap

/*
  FUSE: Filesystem in Userspace
  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
  Copyright (C) 2011       Sebastian Pipping <sebastian@pipping.org>
  This program can be distributed under the terms of the GNU GPL.
  See the file COPYING.
*/
#define FUSE_USE_VERSION 30
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef linux
/* For pread()/pwrite()/utimensat() */
#define _XOPEN_SOURCE 700
#endif
#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <sys/time.h>
#ifdef HAVE_SETXATTR
#include <sys/xattr.h>
#endif
static int xmp_getattr(const char *path, struct stat *stbuf)
{
        int res;
        res = lstat(path, stbuf);
        if (res == -1)
                return -errno;
        return 0;
}
static int xmp_access(const char *path, int mask)
{
        int res;
        res = access(path, mask);
        if (res == -1)
                return -errno;
        return 0;
}
static int xmp_readlink(const char *path, char *buf, size_t size)
{
        int res;
        res = readlink(path, buf, size - 1);
        if (res == -1)
                return -errno;
        buf[res] = '\0';
        return 0;
}
static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
                       off_t offset, struct fuse_file_info *fi)
{
        DIR *dp;
        struct dirent *de;
        (void) offset;
        (void) fi;
        dp = opendir(path);
        if (dp == NULL)
                return -errno;
        while ((de = readdir(dp)) != NULL) {
                struct stat st;
                memset(&st, 0, sizeof(st));
                st.st_ino = de->d_ino;
                st.st_mode = de->d_type << 12;
                if (filler(buf, de->d_name, &st, 0))
                        break;
        }
        closedir(dp);
        return 0;
}
static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
{
        int res;
        /* On Linux this could just be 'mknod(path, mode, rdev)' but this
           is more portable */
        if (S_ISREG(mode)) {
                res = open(path, O_CREAT | O_EXCL | O_WRONLY, mode);
                if (res >= 0)
                        res = close(res);
        } else if (S_ISFIFO(mode))
                res = mkfifo(path, mode);
        else
                res = mknod(path, mode, rdev);
        if (res == -1)
                return -errno;
        return 0;
}
static int xmp_mkdir(const char *path, mode_t mode)
{
        int res;
        res = mkdir(path, mode);
        if (res == -1)
                return -errno;
        return 0;
}
static int xmp_unlink(const char *path)
{
        int res;
        res = unlink(path);
        if (res == -1)
                return -errno;
        return 0;
}
static int xmp_rmdir(const char *path)
{
        int res;
        res = rmdir(path);
        if (res == -1)
                return -errno;
        return 0;
}
static int xmp_symlink(const char *from, const char *to)
{
        int res;
        res = symlink(from, to);
        if (res == -1)
                return -errno;
        return 0;
}
static int xmp_rename(const char *from, const char *to)
{
        int res;
        res = rename(from, to);
        if (res == -1)
                return -errno;
        return 0;
}
static int xmp_link(const char *from, const char *to)
{
        int res;
        res = link(from, to);
        if (res == -1)
                return -errno;
        return 0;
}
static int xmp_chmod(const char *path, mode_t mode)
{
        int res;
        res = chmod(path, mode);
        if (res == -1)
                return -errno;
        return 0;
}
static int xmp_chown(const char *path, uid_t uid, gid_t gid)
{
        int res;
        res = lchown(path, uid, gid);
        if (res == -1)
                return -errno;
        return 0;
}
static int xmp_truncate(const char *path, off_t size)
{
        int res;
        res = truncate(path, size);
        if (res == -1)
                return -errno;
        return 0;
}
#ifdef HAVE_UTIMENSAT
static int xmp_utimens(const char *path, const struct timespec ts[2])
{
        int res;
        /* don't use utime/utimes since they follow symlinks */
        res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
        if (res == -1)
                return -errno;
        return 0;
}
#endif
static int xmp_open(const char *path, struct fuse_file_info *fi)
{
        int res;
        res = open(path, fi->flags);
        if (res == -1)
                return -errno;
        close(res);
        return 0;
}
static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
                    struct fuse_file_info *fi)
{
        int fd;
        int res;
        (void) fi;
        fd = open(path, O_RDONLY);
        if (fd == -1)
                return -errno;
        res = pread(fd, buf, size, offset);
        if (res == -1)
                res = -errno;
        close(fd);
        return res;
}
static int xmp_write(const char *path, const char *buf, size_t size,
                     off_t offset, struct fuse_file_info *fi)
{
        int fd;
        int res;
        (void) fi;
        fd = open(path, O_WRONLY);
        if (fd == -1)
                return -errno;
        res = pwrite(fd, buf, size, offset);
        if (res == -1)
                res = -errno;
        close(fd);
        return res;
}
static int xmp_statfs(const char *path, struct statvfs *stbuf)
{
        int res;
        res = statvfs(path, stbuf);
        if (res == -1)
                return -errno;
        return 0;
}
static int xmp_release(const char *path, struct fuse_file_info *fi)
{
        /* Just a stub.  This method is optional and can safely be left
           unimplemented */
        (void) path;
        (void) fi;
        return 0;
}
static int xmp_fsync(const char *path, int isdatasync,
                     struct fuse_file_info *fi)
{
        /* Just a stub.  This method is optional and can safely be left
           unimplemented */
        (void) path;
        (void) isdatasync;
        (void) fi;
        return 0;
}
#ifdef HAVE_POSIX_FALLOCATE
static int xmp_fallocate(const char *path, int mode,
                        off_t offset, off_t length, struct fuse_file_info *fi)
{
        int fd;
        int res;
        (void) fi;
        if (mode)
                return -EOPNOTSUPP;
        fd = open(path, O_WRONLY);
        if (fd == -1)
                return -errno;
        res = -posix_fallocate(fd, offset, length);
        close(fd);
        return res;
}
#endif
#ifdef HAVE_SETXATTR
/* xattr operations are optional and can safely be left unimplemented */
static int xmp_setxattr(const char *path, const char *name, const char *value,
                        size_t size, int flags)
{
        int res = lsetxattr(path, name, value, size, flags);
        if (res == -1)
                return -errno;
        return 0;
}
static int xmp_getxattr(const char *path, const char *name, char *value,
                        size_t size)
{
        int res = lgetxattr(path, name, value, size);
        if (res == -1)
                return -errno;
        return res;
}
static int xmp_listxattr(const char *path, char *list, size_t size)
{
        int res = llistxattr(path, list, size);
        if (res == -1)
                return -errno;
        return res;
}
static int xmp_removexattr(const char *path, const char *name)
{
        int res = lremovexattr(path, name);
        if (res == -1)
                return -errno;
        return 0;
}
#endif /* HAVE_SETXATTR */
static struct fuse_operations xmp_oper = {
        .getattr        = xmp_getattr,
        .access         = xmp_access,
        .readlink       = xmp_readlink,
        .readdir        = xmp_readdir,
        .mknod          = xmp_mknod,
        .mkdir          = xmp_mkdir,
        .symlink        = xmp_symlink,
        .unlink         = xmp_unlink,
        .rmdir          = xmp_rmdir,
        .rename         = xmp_rename,
        .link           = xmp_link,
        .chmod          = xmp_chmod,
        .chown          = xmp_chown,
        .truncate       = xmp_truncate,
#ifdef HAVE_UTIMENSAT
        .utimens        = xmp_utimens,
#endif
        .open           = xmp_open,
        .read           = xmp_read,
        .write          = xmp_write,
        .statfs         = xmp_statfs,
        .release        = xmp_release,
        .fsync          = xmp_fsync,
#ifdef HAVE_POSIX_FALLOCATE
        .fallocate      = xmp_fallocate,
#endif
#ifdef HAVE_SETXATTR
        .setxattr       = xmp_setxattr,
        .getxattr       = xmp_getxattr,
        .listxattr      = xmp_listxattr,
        .removexattr    = xmp_removexattr,
#endif
};
int main(int argc, char *argv[])
{
        umask(0);
        return fuse_main(argc, argv, &xmp_oper, NULL);
}

Sunday, November 8, 2015

Threading dan membuat music player

Yahoo.. ketemu lagi nih…sekarang kita akan membahas soal pembuatan music player menggunakan dalam terminal menggunakan bantuan vlc.
Pertama-tama, untuk membuat music player kita membutuhkan bantuan multi threading. Apa itu Multi Threading?
Ok mari kita bahas Thread terlebih dahulu.
Thread merupakan bagian dari proses yang ada dalam system operasi. Thread dibagi menjadi dua, Single Threading dan Multi Threading. Single Threading biasanya digunakan untuk mengendailkan jalannya proses. Sedangkan Multi Threading merupankan thread yang lebih dari satu berjalan bersamaan, sehingga kita dapat menjalankan lebih dari satu tugas dalam waktu yang sama.
Kenapa kita butuh Multi Threading?
Dalam music player kita dapat memberikan perintah fungsi seperti pause atau stop pada saat musik berjalan, nah maka dari itu setidaknya ada 2 atau lebih tugas yang dijalankan yaitu musik tersebut dan pemberian perintah.
Mari kita simak kodingan program dibawah ini
#include<stdio.h>
#include<string.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>

pthread_t tid[4];
int length=0;
char song[1024];
void* playAndCount(void *arg){  //fungsi yang dijalankan sebagai thread
     unsigned long i=0;
     pthread_t id = pthread_self();
     if(pthread_equal(id,tid[0])){
           sleep(length);
           system("pkill vlc");
          
     }
     else if(pthread_equal(id,tid[1])){
           char cmd[1024];
           sprintf(cmd,"cvlc Playlist/%s",song);
           system(cmd);         

     }
     else if(pthread_equal(id,tid[2])){
           sleep(length);
           system("pkill -STOP vlc");
     }
    
     else if(pthread_equal(id,tid[3])){
           sleep(length);
           system("pkill -CONT vlc");
     }
    
     return NULL;
}

int main(void)
{
    
close(STDERR_FILENO);
     int i=0;
     int err;
     char opt[1024];
     int flag=0;
     while(1)
     {   
           if(i<=3){
           err = pthread_create(&(tid[i]), NULL, &playAndCount, NULL);
           if(err!=0)
                printf("\ncan't create thread : [%s]", strerror(err));      
           }         
           printf("input options: ");
           scanf("%s",opt);
           if(strcmp(opt,"PLAY")==0){
                printf("input lagu: ");
                scanf("%s",song);               
                if(flag==1) system("pkill vlc");
                i=1;
                flag=1;
           }
           else if(strcmp(opt,"PAUSE")==0){
                printf("input waktu: ");
                scanf("%d", &length);
                i=2;
           }
           else if(strcmp(opt,"CONTINUE")==0){
                printf("input waktu: ");
                scanf("%d", &length);     
                i=3;
           }
           else if(strcmp(opt,"STOP")==0){
                printf("input waktu: ");
                scanf("%d", &length);     
                i=0;
                flag=0;
           }   
           else {
                i=4;
                if(strcmp(opt,"LIST")==0){ system("ls Playlist | grep .mp3");}
                else if(strcmp(opt,"HELP")==0)
                {
                     printf("=================KEYS================\n");
                      printf("PLAY          : Play inputed music\n");
                      printf("PAUSE          : Pause music\n");
                      printf("CONTINUE       : Continue paused music\n");
                      printf("STOP           : Stop music\n");
                      printf("LIST          : Show playlist\n");
                      printf("EXIT          : Exit Player\n");
                      printf("HELP          : Show help keys\n");
                }
                else if(strcmp(opt,"EXIT")==0) {
                      system("pkill vlc");
                      break;
                      }         
           }
     }
     pthread_join(tid[0],NULL);
     pthread_join(tid[1],NULL);
     pthread_join(tid[2],NULL);
     pthread_join(tid[3],NULL);
     return 0;
}


 Di kodingan diatas kita menggunakan 4 thread + 1 proses utama (main) ini dapat dilihat dari pendeklarasian :
pthread_t tid[4];

4 thread ini masing masing digunakan untuk pause music, play music, pause music, dan continue.

fungsi void* playAndCount(void *arg)akan dijalankan sebagai thread.
Untuk Play music kita menggunakan perintah yaitu system(“cvlc nama_lagu”); dapat dilihat di thread tid[1].
Untuk Stop music kita menggunakan system("pkill vlc");  dapat dilihat di thread tid[0]. sleep(length); yang digunakan di thread ini dan beberapa thread lainnya digunakan untuk waiting time sebelum kita menstop(tid[0]) atau pause(tid[2]) atau continue(tid[3]) selama length detik, jadi perintah stop akan dijalankan setelah length detik yang kita inputkan.
Untuk Pause music perintah yang digunakan adalah system("pkill -STOP vlc"); dalam thread[2].
Untuk Continue music yang telah di pause perintah yang digunakan adalah system("pkill -CONT vlc"); dalam thread[3].
Untuk Membuat thread, kita gunakan perintah dibawah ini
err = pthread_create(&(tid[i]), NULL, &playAndCount, NULL);
if(err!=0)
                printf("\ncan't create thread : [%s]", strerror(err));
tid[i] adalah thread yang akan dijalankan yang ada dalam fungsi playAndCount seperti play, pause, continue, dan stop dimana i akan diisi oleh angka 0 sampai 3 di baris bawahnya tergantung apa yang kita inputkan.
Lagu-lagu yang bisa dijalankan adalah lagu yang ada dalam folder Playlist di folder yang sama dengan programnya. Untuk melihat lagu apa saja yang bisa kita putar, dapat menggunakan system("ls Playlist | grep .mp3"); dimana perintah tersebut akan melist semua file mp3 yang ada dalam folder Playlist. Perintah ini dapat kita jalankan dengan mengetik “LIST” ketika program berjalan.
Karena dalam main kita menggunakan while(1) dimana program itu akan terus berjalan tanpa henti, maka kita perlu perintah untuk keluar dengan menggunakan perintah break; yang ada dalam pilihan “EXIT”.
Perintah ptherad_join yang ada setelah while seperti dibawah ini,
     pthread_join(tid[0],NULL);
     pthread_join(tid[1],NULL);
     pthread_join(tid[2],NULL);
     pthread_join(tid[3],NULL);
digunakan untuk menunggu thread lain yang sedang berjalan agar selesai terlebih dahulu sebelum keluar dari program sehingga tidak terjadi error.

Demikian post kali ini tentang thread dan pembuatan music player… Semoga bermanfaat.. sampai jumpa di post lain…

Sunday, October 18, 2015

What and How to Make Daemon + Converting Picture via Daemon

Yahoo~ ketemu lagi di next post ini… kali ini aku akan membahas tentang daemon. Hmm.. apa itu daemon? Untuk mengetahui lebih lengkapnya, mari kita simak bahasan didalam post ini…
               Daemon adalah sebuah background process yang tidak berinteraksi langsung dengan user. Hmm.. sepertinya susah dipahami, nah mudahnya tuh aku kasih contoh aja, kalian tau virus komputer kan? Virus komputer itu termasuk daemon. Virus komputer berjalan tanpa perlu kita suruh dulu kan? Nah itu daemon :v           
Child process adalah proses yang berasal dari proses utama yaitu bapaknya (Parent process). Daemon merupakan orphan process yaitu child process yang bapaknya sudah mati, jadi parent process perlu dimatikan.Kenapa perlu dimatikan? Parent process biasanya adalah proses yang tampil di layar (terlihat) maka untuk daemon yang bekerja di belakang layar parent process perlu dimatikan agar seolah-olah tidak ada proses yang berjalan tetapi ternyata bekerja di belakang layar.
Untuk mengecek suatu proses biasanya dapat dilihat melalui task manager dalam windows. Dan untuk linux bisa dilakukan melalui terminal dengan menulis command “ps aux” (tanpa tanda petik).
Daripada bingung cuma bayangin, mending kita coba buat aja…
Cara membuat Daemon :
1.      Membuat child process dan mematikan parent process

//Untuk membuat child process kita menggunakan forking parent process
pid_t pid, sid;
pid = fork();
//Lalu jika proses berhasil dibuat, maka parent process akan dimatikan
if(pid < 0){ exit(EXIT_FAILURE);}
if(pid > 0){ exit(EXIT_SUCCESS);}
              
2.      Mengubah mode file menggunakan unmask(0) untuk mendapat akses penuh dari file yang dibuat daemon
umask(0);
3.      Membuat sesi ID baru
sid = setsid();
if(sid < 0){ exit(EXIT_FAILURE);}
4.      Mengubah direktori kerja ke direktori yang pasti ada seperti root
if((chdir("/")) < 0){ exit(EXIT_FAILURE);}
5.      Menutup stdin stdout stderr (file descriptor) karena daemon tidak menggunakan kendali terminal
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);

6.  Membuat isi daemon

while(1)
{
 //tulis fungsi daemon disini
}

exit(EXIT_SUCCESS);

Nah itulah langkah-langkah membuat daemon.
Masih bingung daemon apa yang mau dibuat? Ok, dibawah ini akan diberikan contoh daemon untuk linux dengan bahasa C yaitu cara mengedit gambar jpg menjadi abu-abu (grayscale) hanya dengan membuka gambar tersebut… Menarik bukan? Ok, persiapkan alat-alat yang dibutuhkan dahulu..
1.      Laptop ber OS linux  yang terinstall imagemagick
Imagemagick adalah suatu aplikasi untuk linux dan sejenisnya untuk editing foto sederhana seperti merubah warna, crop, dll. Image magick ini dapat diinstall dengan menuliskan perintah “sudo apt-get install imagemagick” (tanpa tanda kutip) dalam terminal di laptop yang memiliki koneksi internet. Jika mendapat kesulitan lain, kita bisa meminta bantuan mbah gugel :v
Setelah terinstall, kita bisa mencoba mengetikan beberapa contoh command dibawah ini di terminal untuk mengconvert suatu gambar

a.      Membuat grayscale dari suatu gambar

convert gambar.jpg -colorspace Gray gambar_hasil.jpg

b.      Mengubah gambar menjadi hitam putih

convert gambar.jpg -monochrome gambar_bw.jpg

c.      Mengubah ukuran gambar

convert gambar.jpg -resize 50% gambar_kecil.jpg
                                                           
2.       Sebuah daemon
3.      Sebuah kursi yang nyaman (bisa lesehan juga :v) ditambah snack dan minuman :v
Ok setelah semua siap, mari kita mulai…
1.      Membuat file sementara untuk menyimpan isi proses yang berjalan dan membukanya di C
FILE *flietemp;
while(1)
{   //masuk ke daemon
//membuat file sementara temp.txt (jika belum ada)
//perintah system (…); berguna untuk menjalankan command bash dalam C
system("touch /home/nama_user/temp.txt");
//memasukan proses (ps aux) kedalam file
system("ps -aux > /home/nama_user/temp.txt");
//membuka file temp
flietemp = fopen("/home/nama_user/temp.txt","r");
             . . . (2-5). . .
             }

2.      Mengecek dalam proses  apakah ada proses membuka gambar (proses ini ditandai dengan adanya nama proses “… eog nama_file”
Contoh penulisan proses membuka gambar

yozora   18401  0.1  1.0 608364 36608 ?        Sl   18:08   0:00 eog/home/yozora/Pictures/gambar.jpg

. . .
//membaca perbaris proses(ps) yang sudah dipindahkan ke temp
while (fgets(ps[i],1024,flietemp)!=NULL)
              {
                    int j=strlen(ps[i])-1;
                    int k;
                    //digunakan untuk menandai ada tidaknya proses membuka gambar jpg
                    int flag =0;
                    int l=0;
                    //membaca setiap karakter dalam baris  
                    for(k=0;k<=j;k++)
                    {     
                    //jika kita menemukan kata eog maka ada proses membuka gambar
                           if(ps[i][k]=='e' && ps[i][k+1]=='o' && ps[i][k+2]=='g')
{
                           flag = 1;
                           //lompat ke direktori & nama file
                           k=k+4;
                           }
                           . . .(3). .
                    }
                    . . .(4). .
             }
                   
      
3.      Jika kita sudah menemukan proses tersebut  maka kita perlu menyimpan direktori dan nama file tersebut dan memastikan file gambar tersebut adalah jpg
. . .
if(flag==1)
   //untuk memastikan file tersebut berekstensi .jpg
if(ps[i][k] =='.' && ps[i][k+1] == 'j'&& ps[i][k+2] == 'p'&& ps[i][k+3] == 'g')
             {
                    flag=3;
                    //keluar dari loop pembacaan karakter dalam baris
                    k=j+1;
                    //memotong nama file mulai dari .jpg
                    savename[l]='\0';
             }
             //untuk file jpg yang berekstensi JPG
              else if(ps[i][k] =='.' && ps[i][k+1] == 'J'&& ps[i][k+2] == 'P'&& ps[i][k+3] == 'G')
              {
                    flag=4;
                    k=j+1;
                    savename[l]='\0';
              }
             else
              {
                    //menyimpan direktori dan nama file per karakter
                    savename[l] = ps[i][k];
                    l++;
              }
   }

4.      Setelah mendapat direktori dan nama file mulailah menjalankan perintah convert (yang sudah dijelaskan sebelumnya)

char cmd[1024];
             strcpy(cmd,"");
             //menuliskan perintah convert dalam sebuah string (flag 3 untuk .jpg)
             if(flag == 3) sprintf(cmd,"convert %s.jpg -colorspace Gray %s_copy.jpg",savename,savename);
             //flag 4 untuk file .JPG
             if(flag == 4) sprintf(cmd,"convert %s.JPG -colorspace Gray %s_copy.JPG",savename,savename); 
             //jalankan perintah convert     
             if (flag>=3) system(cmd);
   //go to the next line
i++;

5.      Menutup file temp

fclose(flietemp);
              system("rm /home/yozora/temp.txt");
      
6.  SELESAI
Jadi intinya setelah kita menjalankan daemon, saat kita membuka file jpg akan otomatis terbuat file _copy yang berupa hasil convert grayscale dari gambar tersebut. Ini akan terus berlanjut hingga daemon dimatikan dengan menulis di terminal “pkill nama_daemon” tanpa tanda kutip.
Untuk Script lengkapnya dapat dilihat dibawah ini

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>

int main(void)
{

pid_t pid, sid;
pid = fork();

if(pid < 0){ exit(EXIT_FAILURE);}
if(pid > 0){ exit(EXIT_SUCCESS);}

umask(0);

sid = setsid();
if(sid < 0){ exit(EXIT_FAILURE);}

if((chdir("/")) < 0){ exit(EXIT_FAILURE);}

close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);


FILE *flietemp;
while(1)
{
       system("touch /home/yozora/temp.txt");
       system("ps -aux > /home/yozora/temp.txt");
       flietemp = fopen("/home/yozora/temp.txt","r");
       int i=0;
       char ps[1024][1024];
       char savename[1024];
       strcpy(savename," ");
       while (fgets(ps[i],1024,flietemp)!=NULL)
       {
             int j=strlen(ps[i])-1;
             int k;
             int flag =0;
             int l=0;    
             for(k=0;k<=j;k++)
             {     
                    if(ps[i][k]=='e' && ps[i][k+1]=='o' && ps[i][k+2]=='g') {
                           flag = 1;
                           k=k+4;
                          
                    }
                    if(flag==1)
                    {
                           if(ps[i][k] =='.' && ps[i][k+1] == 'j'&& ps[i][k+2] == 'p'&& ps[i][k+3] == 'g')
                           {
                                 flag=3;
                                 k=j+1;
                                 savename[l]='\0';
                           }
                           else if(ps[i][k] =='.' && ps[i][k+1] == 'J'&& ps[i][k+2] == 'P'&& ps[i][k+3] == 'G')
                           {
                                 flag=4;
                                 k=j+1;
                                 savename[l]='\0';
                           }
                           else
                           {
                                 savename[l] = ps[i][k];
                                 l++;
                           }
                    }
             }
             char cmd[1024];
             strcpy(cmd,"");
             if(flag == 3) sprintf(cmd,"convert %s.jpg -colorspace Gray %s_copy.jpg",savename,savename);
             if(flag == 4) sprintf(cmd,"convert %s.JPG -colorspace Gray %s_copy.JPG",savename,savename);        
             if (flag>=3)
             system(cmd);
             i++;
       }
       fclose(flietemp);
       system("rm /home/yozora/temp.txt");    
}
exit(EXIT_SUCCESS);
}