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 :)
luvne.com resepkuekeringku.com desainrumahnya.com yayasanbabysitterku.com