Sabtu, 28 November 2015

FUSE: Membuat File Backup Otomatis


Filesystem in Userspace (FUSE) merupakan mekanisme sistem operasi untuk sistem operasi Unix-like yang memungkinkan pengguna tidak ber-hak istimewa menciptakan file system mereka sendiri tanpa mengubah kode kernel. Hal ini dicapai dengan menjalankan kode file system di userspace, sedangkan modul FUSE hanya menyediakan "jembatan" untuk antarmuka kernel yang sebenarnya.

Kegunaan fuse ada banyak, salah satunya adalah yang saya sebutkan diatas. Kegunaan lainnya adalah untuk networking. Contohnya adalah google-drive-ocalmfuse, yang memungkinkan user untuk me-mount google drive ke linuxnya, sehingga memungkinkan mengaksesnya seakan-akan google drive tersebut adalah local storage. Contoh lainnya adalah PNGDrive, yang menyimpan data – data di filesystem tersebut ke sebuah gambar berformat .PNG, dan menggunakan file .PNG tersebut seakan-akan ia adalah sebuah filesystem

Untuk memulai membuat FUSE anda sendiri, anda harus men-download dan meng-install modul FUSE terlebih dahulu yang dapat anda dapatkan di http://fuse.sourceforge.net/ . lalu anda bisa mendownload template FUSE standar di http://fuse.sourceforge.net/doxygen/fusexmp_8c.html . dari template tersebut anda bisa berkreasi untuk melakukan hal – hal semau anda.  Sebagai contoh, saya ingin setiap file yang saya buka secara otomatis membuat file backupnya ( dengan format <namafile>.bak). Apabila file .bak dibuka maka akan muncul notifikasi peringatan file tersebut adalah file backup dan file tersebut tidak dapat dibuka.

Penasaran bagaimana source codenya, berikut source code yang saya gunakan

#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 <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>
#include <dirent.h>
#endif

static const char *dirpath = "/home/exod/Downloads/";
char cbackup[100]=".bak";
char lastaccess[100],lastaccessbackup[100];
char lastunlink[100],lastunlinkbackup[100];
FILE *fin,*from,*to,*temp;
char buffer[BUFSIZ];
size_t sizetemp;

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;
}

static int xmp_access(const char *path, int mask){
 int res;
 char fpath[1000];
 sprintf(fpath,"%s%s",dirpath,path);
 res = access(fpath, mask);
 if (res == -1)
  return -errno;

 strcpy(lastaccess,"NULL");
 strcpy(lastaccessbackup,"NULL");
 return 0;
}

static int xmp_readlink(const char *path, char *buf, size_t size){
 int res;
 char fpath[1000];
 sprintf(fpath,"%s%s",dirpath,path);
 res = readlink(fpath, 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;
 char fpath[1000];
 sprintf(fpath,"%s%s",dirpath,path);
 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;
}

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 */
 char fpath[1000];
 sprintf(fpath,"%s%s",dirpath,path);
 if (S_ISREG(mode)) {
  res = open(fpath, O_CREAT | O_EXCL | O_WRONLY, mode);
  if (res >= 0)
  res = close(res);
 } 
 else if (S_ISFIFO(mode))
  res = mkfifo(fpath, mode);
 else
  res = mknod(fpath, mode, rdev);
 if (res == -1)
  return -errno;

 return 0;
}

static int xmp_mkdir(const char *path, mode_t mode){
 int res;
 char fpath[1000];
 sprintf(fpath,"%s%s",dirpath,path);
 res = mkdir(fpath, mode);
 if (res == -1)
  return -errno;
 return 0;
}

static int xmp_unlink(const char *path){
 int res;
 char fpath[1000];
 sprintf(fpath,"%s%s",dirpath,path);
 res = unlink(fpath);
 if (res == -1)
 return -errno;
 strcpy(lastunlink,fpath);
 strcpy(lastunlinkbackup,fpath);
 strcat(lastunlinkbackup,cbackup);
 xmp_unlink(lastunlinkbackup);
 
 return 0;
}

static int xmp_rmdir(const char *path){
 int res;
 char fpath[1000];
 sprintf(fpath,"%s%s",dirpath,path);
 res = rmdir(fpath);
 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;
 char fpath[1000];
 sprintf(fpath,"%s%s",dirpath,path);
 res = chmod(fpath, mode);
 if (res == -1)
 return -errno;
 return 0;
}

static int xmp_chown(const char *path, uid_t uid, gid_t gid){
 int res;
 char fpath[1000];
 sprintf(fpath,"%s%s",dirpath,path);
 res = lchown(fpath, uid, gid);
 if (res == -1)
 return -errno;
 return 0;
}

static int xmp_truncate(const char *path, off_t size){
//Change the size of a file
 int res;
 char fpath[1000];
 sprintf(fpath,"%s%s",dirpath,path);
 res = truncate(fpath, 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;
 char fpath[1000];
 sprintf(fpath,"%s%s",dirpath,path);
 /* don’t use utime/utimes since they follow symlinks */
 res = utimensat(0, fpath, 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,len;
 char fpath[1000];
 sprintf(fpath,"%s%s",dirpath,path);

 strcpy(lastaccess,fpath);
 strcpy(lastaccessbackup,fpath);
 strcat(lastaccessbackup,cbackup);

 len=strlen(lastaccess);
 if ((len >= 2) && strcmp(&(lastaccess[len - 4]), ".bak") == 0){
  char command[100];
        sprintf(command,"zenity --error --text='File yang anda buka adalah file backup. File tidak bisa diubah maupun disalin kembali'");
        system(command);
        return 1;
 }

 res = open(fpath, fi->flags);
 if (res == -1)
  return -errno;

 if ((len >= 2) && strcmp(&(lastaccess[len - 4]), ".bak") != 0){
  if(access(lastaccessbackup,F_OK)==0) remove(lastaccessbackup);

  from=fopen(lastaccess,"rb");
  to=fopen(lastaccessbackup,"wb");
  char a;
  while(1){
   a=fgetc(from);
   if(!feof(from))
    fputc(a,to);
   else break;
  }
    
  char command[100];
  sprintf(command,"chmod 444 '%s'",lastaccessbackup);
  system(command);
 }

 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;
 char fpath[1000];
 sprintf(fpath,"%s%s",dirpath,path);
 fd = open(fpath, 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;
 
 while((sizetemp = fread(buffer, 1, BUFSIZ, from)))
 {
  fwrite(buffer, 1, sizetemp, to);
 }
 
 char fpath[1000];
 sprintf(fpath,"%s%s",dirpath,path);
 fd = open(fpath, 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)
//Get file system statistics
{
 int res;
 char fpath[1000];
 sprintf(fpath,"%s%s",dirpath,path);
 res = statvfs(fpath, stbuf);
 if (res == -1)
 return -errno;
 return 0;
}

static int xmp_release(const char *path, struct fuse_file_info *fi)
/*Release an open file
Release is called when there are no more references to an open file: 
all file descriptors are closed and all memory mappings are unmapped.*/
{
 /* Just a stub.  This method is optional and can safely be left
 unimplemented */
 char fpath[1000];
 sprintf(fpath,"%s%s",dirpath,path);
 (void) fpath;
 (void) fi;
 return 0;
}

static int xmp_fsync(const char *path, int isdatasync,
struct fuse_file_info *fi) /Synchronize file contents
{
 /* Just a stub.  This method is optional and can safely be left
 unimplemented */
 char fpath[1000];
 sprintf(fpath,"%s%s",dirpath,path);
 (void) fpath;
 (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)
//Allocates space for an open file
{
 int fd;
 int res;
 (void) fi;
 char fpath[1000];
 sprintf(fpath,"%s%s",dirpath,path);
 if (mode)
  return -EOPNOTSUPP;
 fd = open(fpath, 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)
//Set extended attributes 
{
 char fpath[1000];
 sprintf(fpath,"%s%s",dirpath,path);
 int res = lsetxattr(fpath, 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)
//Get extended attributes
{
 char fpath[1000];
 sprintf(fpath,"%s%s",dirpath,path);
 int res = lgetxattr(fpath, name, value, size);
 if (res == -1)
 return -errno;
 return res;
}

static int xmp_listxattr(const char *path, char *list, size_t size)
//List extended attributes
{
 char fpath[1000];
 sprintf(fpath,"%s%s",dirpath,path);
 int res = llistxattr(fpath, list, size);
 if (res == -1)
 return -errno;
 return res;
}

static int xmp_removexattr(const char *path, const char *name)
{
 char fpath[1000];
 sprintf(fpath,"%s%s",dirpath,path);
 int res = lremovexattr(fpath, 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);
}

Ganti bagian static const char *dirpath = "/home/exod/Downloads" dengan static const char *dirpath = "/home/[nama user]/Downloads"
Simpan kemudian compile dengan menggunakan perintah: gcc -Wall [nama file].c `pkg-config fuse --cflags --libs` -o [nama file]

Kemudian buat sebuah direktori, misalnya: /tmp/fuse
Sekarang yang anda lakukan adalah me mount program anda ke /tmp/fuse. Yaitu dengan cara :  ./[nama file] /tmp/fuse
Maka semua isi direktori /home/[nama user]/Downloads akan dimount ke direktori /tmp/fuse
Coba masuk ke /tmp/fuse dan jalankan perintah ls, maka semua isi direktori tersebut akan ditampilkan.

Terlihat bahwa yang diubah dari template adalah beberapa variabel global dan di dalam fungsi open. Karena hanya setiap membuka file akan dibuat backupnya, maka hanya fungsi open yang saya ubah sedikit.

Berikut beberapa istilah atau kegunaan setiap fungsi :
getattr        = xmp_getattr,  => mengambil atribut file
.access         = xmp_access,         => mengambil direktori
.readlink       = xmp_readlink,   => membaca target dari symbolic link
.readdir        = xmp_readdir,       => membaca direktori
.mknod          = xmp_mknod,     => membuka node file
.mkdir          = xmp_mkdir,         => membuat directory
.symlink        = xmp_symlink,   => membua symbolic link
.unlink         = xmp_unlink,       => menghapus file
.rmdir          = xmp_rmdir,           => menghapus directory
.rename         = xmp_rename,     => memperbaharui nama file
.link           = xmp_link,              => menciptakan hardlink ke file
.chmod          = xmp_chmod,      => mengubah permission file
.chown          = xmp_chown,      => mengubah kepemilikan file
.truncate       = xmp_truncate,    => merubah ukuran file
.utimens        = xmp_utimens,    => mengubah akses dan modifikasi waktu dari file dengan resolusi nanosecond
.open           = xmp_open,           => membuka file
.read           = xmp_read,              => membaca data dari file yang dibuka
.write          = xmp_write,            => menulis data ke file yang telah dibuka
.release        = xmp_release,        => melepaskan file yang sudah dibuka
.fsync          = xmp_fsync,           => sinkronisasi isi file

Cukup sekian ilmu yang dapat saya bagi hari ini. Jika ada kesalahan saya mohon maaf. Semoga bermanfaat :)

Jumat, 06 November 2015

IPC (Interprocess Communincation)


Interprocess Communication adalah cara atau mekanisme pertukaran data antara satu proses dengan proses lainnya, baik itu proses yang berada di dalam komputer yang sama, atau komputer jarak jauh yang terhubung melalui jaringan.

a. Pipe
    Pipe merupakan komunikasi sequensial antar proses yang saling terelasi, namun pipe memiliki kelemahan yaitu hanya bisa digunakan untuk komunikasi antar proses yang saling berhubungan, dan komunikasinya yang dilakukan adalah secara sequensial.
b. Message Queue
    Sistem berkirim pesan adalah proses komunikasi antar bagian sistem untuk membagi variabel yang dibutuhkan. Proses ini menyediakan dua operasi yaitu mengirim pesan dan menerima pesan.
c. Shared Memory
    Sistem Berbagi Memori merupakan salah satu cara komunikasi antar proses dengan cara mengalokasikan suatu alamat memori untuk dipakai berkomunikasi antar proses. Alamat dan besar alokasi memori yang digunakan biasanya ditentukan oleh pembuat program. Pada metode ini, sistem akan mengatur proses mana yang akan memakai memori pada waktu tertentu sehingga pekerjaan dapat dilakukan secara efektif.
d. Socket
    Bentuk dari komunikasi yaitu UDP dan TCP menggunakan abstraksi socket yang menyediakan endpoint untuk komunikasi antar proses. Socket bisa dijalankan di berbagai platform(BSD UNIIX, UNIX, Linux, Windows, & Machintos OS).

Pada kesemptan kali ini saya akan membagikan ilmu tentang IPC dengan menggunakan shared memory.
Anda dapat menggunakan shared memory seakan akan sebuah variabel yang dapat dibaca dan ditulis oleh proses lain, sambil menganggapnya sebagai sebuah variabel. Walaupun bila anda ingin sedikit berkreasi, anda dapat menggunakan sebuah shared memory untuk diisi beberapa variabel didalamnya dengan menggunakan struct.
  1. Pertama anda akan membuat key dahulu, key ini dibuat dengan fungsi ftok(const char *path, int id). Dengan *path adalah direktori file kosong dan id adalah angka random. Bila anda ingin membuat shared memory yang dapat diakses dari 2 process yang beda, pastikan semua parameter key sama. Return value dari fungsi ftok adalah bertipe key_t dan harus disimpan karena akan digunakan untuk proses selanjutnya.
  2. Kedua adalah dengan membuat shared memory nya, dengan fungsi shmget(key_t key, size_t size, int shmflag). Key adalah key yang didapat dari fungsi ftok diatas. Size adalah besar shared memory dalam byte(jadi bila anda ingin membuat array integer yang berisi 2 integer, atau int arr[2], maka buatlah sizenya menjadi 2*sizeof(int)). Dan shmflag adalah flag. Flag adalah persmission yang di OR dengan IPC_CREAT. Permission adalah sebuah 0 diikuti dengan permission yang seperti di chmod, jadi misal semua orang hanya boleh membacanya, maka permissionnya 0444. Dan IPC_CREAT menandakan bahwa anda membuat IPC baru. Bila IPC tersebut sudah ada, maka IPC_CREAT akan diabaikan dan anda akan tersambung ke IPC yang sudah ada tersebut. Return value dari shmget adalah sebuah integer yang berisi file descriptor dari shared memory tersebut, simpanlah file descriptor ini dalam sebuah variabel. Dalam tutorial ini, file descriptor ini akan disimpan dalam variabel shmid
  3. Ketiga adalah attach, jadi setelah shared memorynya dibuat, anda mengattachnya ke program anda untuk dapat digunakan. Caranya menggunakan fungsi shmat(int shmid, void *shmaddr, int shmflag);. Dengan shmid adalah return value dari shmget diatas, shmaddr bisa diisi dengan alamat memory, yang bsia saja diisikan dengan 0 dan biarkan OS yang memilih alamatnya, dan jangan lupa untuk di typecast menjadi pointer to void.ehkan baca dan tulis. Return value dari shmat ini adalah pointer to void. Yang tinggal anda typecast menjadi type yang anda inginkan saat menggunakan.
  4. Sekarang Shared memory sudah jadi dan anda bisa mengaksesnya semau anda. Anda dapat membaca dan menulisnya sebagai sebuah string, atau membuatnya menjadi integer, anda bahkan dapat menggunakannya untuk mempassing struct buatan anda sendiri.
  5. Sekarang bila anda sudah selesai dan ingin menutupnya, gunakan shmdt(void *shmaddr);. Dengan shmaddr adalah return value dari shmat.
  6. Terakhir bila anda ingin menghapus shared memory ini, anda dapat menggunakan fungsi shmctl(int shmid, IPC_RMID, NULL);. Dengan shmid adalah return value dari shmget.
Bila anda ingin melihat seluruh IPC yang ada di pc anda, anda dapat mengetikkan “ipcs” pada terminal dan terminal akan menampilkan seluruh ipc yang ada. Dan bila anda ingin menghapusnya, anda tinggal menggunakan perintah “ipcrm”

Contoh penggunaan IPC shared memory yaitu dalam sebuah aplikasi server dan client pada sebuah aplikasi jual beli. Ketika client membeli barang otomatis data pada shared memory akan langsung terupdate dan dapat diakses oleh server. Demikian pula saat server menambah stok barang. Berikut contoh aplikasi server dan client jual beli.

/* SERVER */

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <unistd.h>
#define  NOT_READY  0
#define  READY      1
#define clear system("clear")

struct Memory {
    int jumlahbrg[20];
    char namabrg[20][20];
    int banyakbrg;
};

int menu(){
    int pilih;
    clear;
    printf("\n-------======OINK COMPUTER======------\n");
    printf("|                                     |\n");
    printf("| 1. Lihat Stok                       |\n");
    printf("| 2. Tambah Stok                      |\n");
    printf("|_____________________________________|\n\n");
  
    scanf("%d",&pilih);
    return pilih;
}

int  main(){
    key_t sharedKEY;
    int sharedID, flag=1, jumlah=0, status=0, pilih;
    int i;
    struct Memory *data;
    sharedKEY = ftok(".", 'a'); 
    sharedID = shmget(sharedKEY, sizeof(struct Memory), IPC_CREAT | 0666);
    
    if (sharedID < 0) {
        printf("*** shmget error (server) ***\n");
        exit(1);
    }
    data = (struct Memory *) shmat(sharedID, NULL, 0);
    data->banyakbrg=6;
        for(i=0;i<data->banyakbrg;i++){
            data->jumlahbrg[i]=3;
        }    
        i=0;
        strcpy(data->namabrg[i++],"RAM");
        strcpy(data->namabrg[i++],"hardisk");
        strcpy(data->namabrg[i++],"motherboard");
        strcpy(data->namabrg[i++],"PSU");
        strcpy(data->namabrg[i++],"VGA");
        strcpy(data->namabrg[i++],"processor");
    do{
    char command[20];
    pilih=menu();
    if(pilih==1){
        do{
        system("clear");
        printf("\n\nNB : Ketik REFRESH untuk melihat stock terbaru\n");
        printf("\n_________________________\n");
        printf("| STOCK YANG TERSEDIA   |\n");
        puts("");
        puts("");
        for(i=0;i<data->banyakbrg;i++){
            printf(" %s : %d\n",data->namabrg[i],data->jumlahbrg[i]);
        }
        printf("-------------------------\n");
        scanf("%s",command);
        }while(strcmp(command,"back")!=0);
    }
    else if(pilih==2){
        printf("\nPilih barang dan jumlah yang akan ditambah :\n");
        do{
            if(status==1) printf("   Berhasil ditambah\n");
            else if(status==3) printf("   Barang baru berhasil ditambah\n");
            status=0;
            scanf("%s",command);
            if(strcmp(command,"back")==0) continue;
            else {
                for(i=0;i<data->banyakbrg;i++){
                    if(strcmp(command,data->namabrg[i])==0){
                        scanf("%d",&jumlah);
                        status=1;
                        data->jumlahbrg[i]+=jumlah;
                        break;
                    }
                }
            }
            if(i==data->banyakbrg) {
                status=3;
                scanf("%d",&jumlah);
                strcpy(data->namabrg[data->banyakbrg],command);
                data->jumlahbrg[data->banyakbrg++]=jumlah;
            }
        }while(strcmp(command,"back")!=0);
    }
    else break;
    clear;
    }while(pilih!=0);

    printf("   Server exits...\n");
    shmctl(sharedID, IPC_RMID, NULL);
    exit(0);
} 

Untuk client sebenarnya tidak terlalu berbeda dengan server, namun perbedaannya terletak pada shmget. Pad client tidak perlu menambahkan opsi IPC_CREAT karena ipc sudah dibuat oleh server. Berikut contoh clientnya

/* Client */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <unistd.h>
#define  NOT_READY  0
#define  READY      1
#define clear system("clear")

struct Memory {
    int jumlahbrg[20];
    char namabrg[20][20];
    int banyakbrg;
};

int menu(){
    int pilih;
    clear;
    printf("\n-------======OINK COMPUTER======------\n");
    printf("|                                     |\n");
    printf("| 1. Lihat Stok                       |\n");
    printf("| 2. Beli Barang                      |\n");
    printf("|_____________________________________|\n\n");
 
    scanf("%d",&pilih);
    return pilih;
}

int  main(void){
    key_t sharedKEY;
    int sharedID;
    struct Memory *data;
    int jumlah=0, status=0, pilih, i;
   
    sharedKEY = ftok(".", 'a');
    sharedID = shmget(sharedKEY, sizeof(struct Memory), 0666);
    if (sharedID < 0) {
        printf("*** shmget error (client) ***\n");
        exit(1);
    }
    data = (struct Memory *) shmat(sharedID, NULL, 0);

    do{
    char command[20];
    pilih=menu();
    if (pilih==1) {
        do{
        clear;
        printf("\n\nNB : Ketik REFRESH untuk melihat stock terbaru\n");
        printf("\n_________________________\n");
        printf("| STOCK YANG TERSEDIA   |\n");
        puts("");
        puts("");
        for(i=0;i<data->banyakbrg;i++){
            printf(" %s : %d\n",data->namabrg[i],data->jumlahbrg[i]);
        }
        printf("-------------------------\n");
        scanf("%s",command);
        }while(strcmp(command,"back")!=0);
    }
    else if(pilih==2){
        printf("\nPilih barang dan jumlah yang akan dibeli :\n");
        do{
            if(status==1) printf("   Pembelian sukses\n");
            else if(status==2) printf("   Stok Habis/Tidak Mencukupi\n");
            else if(status==3) printf("   Tidak ada barangnya\n");
            status=0;

            scanf("%s",command);
            if(strcmp(command,"back")==0) continue;
            else {
                for(i=0;i<data->banyakbrg;i++){
                    if(strcmp(command,data->namabrg[i])==0){
                        scanf("%d",&jumlah);
                        if(data->jumlahbrg[i]-jumlah>=0){
                            status=1;
                            data->jumlahbrg[i]-=jumlah;
                        }
                        else status=2;    
                        break;
                    }
                }
            }

            if(i==data->banyakbrg) status=3;
        }while(strcmp(command,"back")!=0);
    }

    }while(pilih!=0);
    shmdt((void *) data);          
    exit(0);
}

Cukup sekian ilmu yang dapat saya bagi hari ini. Jika ada kesalahan saya mohon maaf. Semoga bermanfaat :)

Sabtu, 17 Oktober 2015

PROSES DAN DAEMON

Yosh saya akan share ilmu mengenai manajemen proses pada linux serta pembuatan daemon menggunakan bahasa C.

Konsep Proses pada Sistem Linux

Proses adalah program tunggal yang sedang dalam keadaan dieksekusi pada alamat virtual. Process merupakan keadaan ketika sebuah program sedang di eksekusi. Setiap proses juga memiliki PID atau Process ID yang merupakan nomor unik yang dapat digunakan untuk berinteraksi dengan proses bersangkutan.
Cara menampilkan process tersebut adalah dengan mengetik "ps" (tanpa tanda petik di terminal)


Untuk melihat faktor/elemen lainnya, gunakan option –u (user).  

Mencari proses lainnya gunakan opsi a, au dan aux

Melihat proses yang sedang berjalan
  
Menghentikan suatu proses/job
  1. kill %<nomor job> contoh : kill %1
  2. kill <PID> contoh : kill 1908
  3. pkill <nama proses> contoh : pkill firefox
  4. pkillall <nama proses> contoh : pkillall firefox
Daemon adalah proses yang berjalan di balik layar (background) dan tidak berinteraksi langsung dengan user melalui standard input/output.

Cara Membuat Daemon

Ada 6 proses pembuatan Daemon

   1.  Fork Parent Processs dan penghentian Parent Process Langkah pertama dari pembuatan daemon adalah menspawn proses menjadi induk dan anak dengan melakukan forking,  kemudian membunuh proses induk. Proses induk yang mati akan menyebabkan sistem operasi mengira bahwa proses telah selesai
 pid_t pid, sid;
 pid=fork();
 if (pid < 0){
   exit(EXIT_FAILURE);
 }
 if (pid > 0){
  //catat PIP proses anak ke sebuah file
   exit(EXIT_SUCCESS);
 } //jika pembuatan proses berhasil, maka parent proses akan dimatikan
     2. Mengubah mode file menggunakan UMASK(0);
Untuk menulis beberapa file (termasuk logs) yang dibuat oleh daemon, mode file harus diubah untuk memastikan bahwa file tersebut dapat ditulis dan dibaca secara benar. Pengubahan mode file menggunakan implementasi umask().
     3. Membuat Unique Session ID (SID)  Child Process harus memiliki unik SID dari kernel untuk dapat beroperasi. Sebaliknya, Child process menjadi Orphan Proses pada system. Tipe pid_t yang dideklarasikan pada bagian sebelumnya, juga digunakan untuk membuat SID baru untuk child process.
sid = setsid();
  if(sid<0){
  exit(EXIT_FAILURE);
  }  
   4.Mengubah Directory Kerja Directori kerja yang aktif harus diubah ke suatu tempat yang telah pasti akan selalu ada. Pengubahan tempat direktori kerja dapat dilakukan dengan implementasi fungsi chdir (). Fungsi chdir() mengembalikan nilai -1 jika gagal.
 if((chdir("/"))<0) {
        exit(EXIT_FAILURE);
    }
    5. Menutup File Descriptor Standar Salah satu dari langkah terakhir dalam mengeset daemon adalah menutup file descriptor standar (STDIN, STDOUT, STDERR). Karena daemon tidak perlu menggunakan kendali terminal, file descriptor dapat berulang dan berpotensi memiliki bahaya dalam hal keamanan. Untuk mengatasi hal tersebut maka digunakan implemtasi fungsi close().
 close(STDIN_FILENO);
 close(STDERR_FILENO);
 clode(STDOUT_FILENO);
   6. Membuat Loop utama (inti kerja dari daemon)  Daemon bekerja dalam jangka waktu tertentu, sehingga diperlukan sebuah looping. 
  
while(1){
    sleep(5)
}
exit(EXIT_SUCCES);

Berikut contoh implementasi daemon secara lengkap
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <dirent.h>

int main(){

	pid_t pid, sid;
	pid=fork();
	if (pid < 0){
	 	exit(EXIT_FAILURE);
	}
	if (pid > 0){
		//catat PIP proses anak ke sebuah file
	 	exit(EXIT_SUCCESS);
	} 
	umask(0);
	sid = setsid();
        if(sid<0){
		exit(EXIT_FAILURE);
	}

	if((chdir("/"))<0) {
		exit(EXIT_FAILURE);
	}
	close(STDIN_FILENO);
	close(STDERR_FILENO);
	close(STDOUT_FILENO);
	while(1){
             sleep(30);
        }
        exit(EXIT_SUCCESS);
  }
Lalu bagaimana contoh penerapannya pada sebuah aplikasi?. Oke saya akan membuatkan contohnya. Saya akan membuat program daemon untuk memindahkan secara otomatis file pada sebuah directory. Berarti hanya terdapat file berekstensi tertentu saja di sebuah direktori. Pada pemrograman bahasa C kita harus menambahkan sebuah file header untuk membaca direktori yaitu dirent.h Berikut kodingannya untuk memindah file selain .doc ke folder lain
 DIR *dp;
struct dirent *ep;
//open folder direktorinya
dp = opendir ("/home/exod/contoh/"); 
if (dp != NULL)
{
    while (ep = readdir (dp)){
       char namanya[100], temp[100];
       /*periksa apakah file tersebut mengandung ekstensi .doc
       periksa panjang string dan cari .doc dalam string*/
       int len = strlen (ep->d_name);
       if ((len >= 2) && strcmp (&(ep->d_name[len - 4]), ".doc") != 0){
           /*tentukan alamat asal dan tujuan direktori
           lalu pindahkan file selain .doc ke folder lain  dengan fungsi rename*/

           snprintf(namanya,sizeof(namanya),"/home/exod/contoh/%s",ep->d_name);
           snprintf(temp,sizeof(temp),"/home/exod/bukandoc/%s", ep->d_name);
           rename(namanya,temp);
       }
   } 
   (void) closedir (dp);
}
else
perror ("Couldn't open the directory");
Cukup sekian tutorial yang dapat saya bagi hari ini. Jika ada kesalahan saya mohon maaf. Semoga bermanfaat :)

    Sabtu, 26 September 2015

    LINUX : Bash Scripting

    Pada kesempatan kali ini, saya akan membagikan sedikit ilmu yang telah saya dapatkan tentang bash scripting. 

    Shell Scripting
     
    Shell script adalah beberapa perintah yang ditulis dengan plain text file. Fungsi utama dari shell scripting adalah mengotomasi perintah - perintah yang sudah biasa kita gunakan sehingga kita tidak perlu menuliskan setiap kali dengan cara yang berulang dan lengkap, tapi cukup dengan fungsi yang telah kita buat. Untuk manualnya $man bash or $man sh.

    Tidak seperti sistem operasi lain yang hanya menyediakan satu atau 2 shell, sistem operasi dari keluarga unix misalnya linux sampai saat ini dilengkapi oleh banyak shell dengan kumpulan perintah yang sangat banyak, sehingga memungkinkan pemakai memilih shell mana yang paling baik untuk membantu menyelesaikan pekerjaannya, atau dapat pula berpindah-pindah dari shell yang satu ke shell yang lain dengan mudah, beberapa shell yang ada di linux antara lain:
    1. Bourne shell(sh),
    2. C shell(csh)
    3. Korn shell(ksh),
    4. Bourne again shell(bash),
    5. dsb.
    Masing - masing shell mempunyai kelebihan dan kekurangan yang mungkin lebih didasarkan pada kebutuhan pemakai yang makin hari makin meningkat, untuk dokumentasi ini shell yang digunakan adalah bash shell dari GNU, yang merupakan pengembangan dari Bourne shell dan mengambil beberapa feature (keistimewaan) dari C shell serta Korn shell, Bash shell merupakan shell yang cukup banyak digunakan pemakai linux karena kemudahan serta banyaknya fasilitas perintah yang disediakan.

    Bash Script

    coba ikuti langkah - langkah berikut:

    1. Masuk ke editor anda, apakah memakai gedit, nano, vi,pico,emacs,dsb...
     
    2. ketikkan perintah berikut

    3. simpan dengan nama file tes
    4. ubah permission file tes menggunakan chmod
    5. jalankan

    Hasilnya


    tanda #!/bin/bash dalam script latihan.sh adalah perintah yang diterjemahkan ke kernel linux untuk mengeksekusi path yang disertakan dalam hal ini program bash pada direktory /bin, sebenarnya tanpa mengikutkan baris tersebut anda tetap dapat mengeksekusi script bash, dengan catatan bash adalah shell aktif. atau dengan mengetikkan bash pada prompt shell.

    tentunya cara ini kurang efisien, menyertakan path program bash diawal script kemudian merubah permission file sehingga dapat anda eksekusi merupakan cara yang paling efisien.

    Membuat Program Bilangan Prima

    Selanjutnya kita akan belajar bagaimana menampilkan bilangan prima lewat bash script. Membuat program untuk bilangan prima di bash script seperti halnya memrogram di bahasa C.

    Input: baris pertama berisi bilangan n
    Output: semua bilangan prima <= n
    Contoh:

    input : 5
    hasil : 2 3 5

    input : 10
    hasil : 2 3 5 7

    Berikut langkah-langkahnya :

    1. Masuk ke editor anda
    2. Buat variabel penampung untuk input yang akan kita isikan

    3. Buat perulangan untuk mengecek dari angka 2 sampai angka ke n (karena 1 bukan angka prima)

    4. Karena bilangan prima hanya dapat dibagi 1 dan bilangan itu sendiri, maka kita buat variabel "cek" untuk menghitung berpa banyak bilangan itu dapat dibagi. 
    5. Buat perulangan lagi untuk mengecek sisa pembagian (bilangan pembagi)

     6. Buat kondisi, jika bilangan tersebut dibagi dan tersisa 0 maka "cek" akan bertambah 1


    7. Buat kondisi, jika cek=2 maka cetak bilangan tersebut di layar

    8. Selesai. Mari kita cek programnya

     Mengarsipkan Folder dan Membuat Log Arsip


    1. Masuk ke editor anda
    2. Di sini saya akan mengarsipkan folder Dokumen. Buat variabel untuk menampung tanggal dan waktu untuk disimpan di file log

    3. Ketikkan perintah berikut
       *penjelasan
         c -> membuat file arsip tar
         v -> Verbose. Artinya kita dapat melihat progress pada saat kita mebuat/mengextract file tar
         z -> membuat file gz yang kita buat
         f -> nama file tar yang kita buat
         echo ..... >> log_file.txt -> menulis tanggal dan waktu dan membuat file log

    4. Jalankan bash scriptnya. Akan terlihat folder dan fie yang terdapat dalam folder dokumen berhasil diarsipkan
      
    5. Mari kita cek file arsipnya. Jika terdapat kedua file tersebut berarti pengarsipan berhasil

    6. Cek isi log_file.txt

     Cukup sekian tutorial yang dapat saya bagi hari ini. Jika ada kesalahan saya mohon maaf. Semoga bermanfaat :)


    luvne.com resepkuekeringku.com desainrumahnya.com yayasanbabysitterku.com