Раздел «Язык Си».OOP-Instrumental_3sem1:

Объекты могут состоять ихз других объектов, как уже реализованных ранее (например string, sstream, ifstream, ofstream и др.), так и из тех, которые были описаны нами.

Простые и сложные объекты могут порождаться используя стековую память функции, в которой они локальные или используя динамическую память ("кучу"). В первом случае при завершении функции объект уничтожается (с использованием дестуктора). Во втором необходимо освобождать память "вручную" с использование оператора delete.

Рассмотрим пример

Список файлов в каталоге

Заголовочный файл. scfile.h

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <pwd.h>
#include <errno.h>
#include <fcntl.h>
#include <dirent.h>
#include <string.h>

#include <iostream>
#include <cstdlib>

using namespace std;


class SystemFile{
  struct passwd *result; 
  string fname;
  struct stat sb;
  int id;
  char mod;
  struct flock lck;
public:
  SystemFile();
  ~SystemFile();
  bool closef();
  unsigned char openf(char*);
  string getUser();
  long getSize();
  bool writef(void*, long);
  bool readf(void*, long);
  void about();
  long numbersOfRecord(long);
  int lock();
  int unlock();

};

// Объекты класса Catalog будут содержать 
// массив файлов - объектов класса System File
class Catalog{
   SystemFile *files;     // указатель на массив файлов
   DIR *dir;              // дескриптор директории 
   unsigned int numbersf; // количество всех файлов в кталоге
   string namedir;        // имя данного каталога
public:
   Catalog();                  // конструктор 
   ~Catalog();                 // деструктор
   unsigned char dopen(char*); // открытие каталога 
   SystemFile operator[](int); // выдает файл из каталога по номеру 
   bool dclose();              // закрыть каталог
   int getNumbers();           // получить количество записей
   void list();                // напечатать содержимое каталога
};

Файл реализации функций класса Catalog catalog.cpp
#include "scfile.h"

Catalog::Catalog(){
   dir=0;      // указатель в 0
   numbersf=0; // количество в 0 
};

Catalog::~Catalog(){
// если дескриптор не 0, значит каталог
// был открыт
  if (dir > 0){
// закрываем все открытые классы
    for (int i = 0; i < numbersf; i++){
      files[i].closef();
    }
// удаляем выделенную память
    delete[] files;
// удаляем каталог
    dclose();
  }

};

unsigned char Catalog::dopen(char* cname){
   
// Связывание дескриптора dir с именем каталога, указанным первым аргументом   
     dir = opendir(cname);
//Проверка открылся ли каталог
     if(errno){
         perror("каталог!!!");
        return errno;
     }
// структура описания файла в каталоге.
 struct dirent *dirrec;
/*
struct dirent {
  ino_t          d_ino;       //inode number 
  off_t          d_off;       // not an offset; see NOTES 
  unsigned short d_reclen;    // length of this record 
  unsigned char  d_type;      // type of file; not supported
                              // by all filesystem types 
  char           d_name[256]; // filename 
 };

*/
// подсчет количества записей в каталоге
    while(dirrec = readdir(dir)){
      numbersf++;
    }
    cout<<numbersf<<endl;
// выделение памяти под массив файлов в каталоге
    files = new SystemFile[numbersf];
    int i = 0;
// дескриптор в конце файла - вернем в начало
    dir = opendir(cname);
// получение записей и занесений их в каталог
    while(dirrec = readdir(dir)){
     // Проверка: файл или каталог
      files[i].openf(dirrec->d_name);
      i++;
    }   
};

void Catalog::list(){
// печать содержимого каталога
     for(int i = 0; i< numbersf; i++)
        files[i].about();

};
SystemFile& Catalog::operator[](int k){
     if(k>-1 && k< numbersf)
     return files[k];
     exit(1);
};
// закрыть каталог
bool Catalog::dclose(){
// если каталог не открывали или
// уже закрыли, ничего не делаем
        if(dir != 0){
// закрываем каталог
         closedir(dir);
// ставим дескриптор в 0, на случай
// если второй раз попробуем вызвать dclose()
         dir=0;
        return 0;
      }
     return 1;      
};

Задачи

Задача 1

Иногда требуется предоставить пользователь очень ограниченный доступ к ресурсам. Например, имеется полностью закрытый каталог: на запись, чтение и исполнение. Но, необходимо обеспечить временный доступ, например к одному файлу этого каталога.

Для компилируемых программ (она запускается самостоятельным процессом, можно установить специальный арибут, который позволит ей работать с правами ее создателя.

Программа запускается с UID пользоваетля, но у нее есть еще и эффективный UID — тот UID, с которым выполняется программа на самом деле. Можно установить специальный SUID, чтобы позволить программе выполняться с правами создателя программы.

SUID не работает для скриптовых программ.

>chmod u + s myprog // будет исполняться с правами владельца myprog
>chmod g + s myprog1 // будет исполняться с правами группы myprog

В первом случае myprog будет запускаться с правами создателя всеми, кто имеет к ней доступ , а во втором myprog1 будет запускаться с правами групп.

  1. Зайти на сайт lk3.mipt.ru пользователем s317001. Создать каталог со своим именем и дать ему права на запись файлов для всех. Создать в этом каталоге каталог dark .
  2. Написать программу с использованием классов SystemFile и Catalog, которая получает uid ( getuid()) пользователя, который ее запустил и выдает список только тех файлов, которые принадлежат этому пользователю.
  3. Изменить пользователя на другие: s31703, s31705 и др. с помощью команды su

    >su s31706
    

    Создать несколько файлов под разными пользователями в каталоге dark.

  4. Указать права для dark на исполнение, чтение и запись только для владельцев этого каталога.
  5. Указать SUID для своей программы. Проверить ее работу запуском разными пользователями.

Задача 2

Каждый процесс может породить свой дочерний процесс. При этом дочерний процесс наследует от родительского все значения переменных, открытые дескрипторы, терминал. Но этот дочерний процесс — совершенно самостоятельный: у него свой pid, своя память и т.д.

Пример использования порожденных процессов.

#include <unistd.h>
#include <iostream>
#include <cstdlib>
#include <sstream>
#include <ctime> // для clock()
#include <fstream>

using namespace std;
int main(){
// строковый поток (можно пользоваться операторами ввода/вывода
  stringstream sp;
// строка
  string st;
  int pid;   // pid текущего процесса
  int  ppid; // pid родительского процесса
// получить время начала работы родительского процесса
  unsigned int start_time =  clock();
//  в цикле получаем запрос для исполнения: w - запись в файл
// r - читать из файла, d -  удалить 
  while(1){
// получаем строку
   cin>>st;
//  если ввели *, то выходим из программы
   if(st == "*") exit(1);
// порождаем детский процесс
// родительский процесс при этом получает pid детского процесса, 
// а в детском pid будет равен 0
   pid = fork();
// если это детский процесс, выходим из цикла,
// а родитель остается в цикле получать команды
// каждую команду будет обрабатывать свой детский процесс
   if(pid == 0) break;   
  }
//  получаем порождения детского процесса
  unsigned int run_time =  clock();
// генерим имя файла для записи действий
  sp<<"log_"<<getpid();
// открываем файл на ввод
  ofstream fo;
// sp.str() возвращает строку, а c_str() от строки возвращает C-строку (массив символов)
  fo.open((sp.str()).c_str()); 
// записываем информацию в файл
  fo<<"fork pid: "<<getpid()<<" at time: "<< run_time -start_time<<endl;

  if(st == "w"){
    fo<<" пишем\n";
  }
  if(st == "s"){
    fo<<" ищем\n";
  }
  if(st == "d"){
    fo<<" удаляем\n";
  }
  fo.close();
}

Написать программу, которая в цикле получает команды:

  1. write — получить имя ТЕКСТОВОГОТ файла с записями дробей и НИКАК НЕ ИЗМЕНЯЯ их записать в бинарный файл;
  2. read — прочитать дроби из бинарного файла, удалить все, у которых в знаменателе 0 и записать все это в файл обратно. При удалении записей изменяем размер файла (trancate()) и проверяем не изменился ли его размер (появились другие записи)
  3. aver — вычисляет среднее арифметическое всех дробей
  4. * — закрывает программу

    Каждая операция выполняется отдельным дочерним процессом. Отразить это в log-файле.

    Заголовочный файл для дробей
    #include <iostream>
    #include <cstdlib>
    #include "sysfile.h"
    #include <fstream>
    
    
    struct Drob{
      char sign; // знак
      int c, n, d; // целая часть, числитель, знаменатель
    };
    
    class Fract{
      Drob d; // дробь
    public:
       Fract();
       Fract(char, int,int,int );
       void set(char,int,int,int);
       Fract& operator+=(const Fract&); // сложение
       Fract& operator+(const Fract&); // сложение
       Fract& operator=(const Fract&); // присваивание
       Fract& operator=(int);          // присваивание
       Fract& operator-(); // унарный минус (перед числом)
       Frac& operator!(); // инвертирование дроби (1/дробь)
       Frac& operator*(const Frac&); // умножение
       Frac& operator/(const Frac&); // деление
       void print();
    };
    
    class FractArr{
      Fract *m; // указатель на массив дробей
      int n;   // размер массива
    public:
      FractArr(); // конструктор
      ~FractArr(); // деструктор
      void getArr(SystemFile&); // прочитать дроби из бинарного файла
      char write(SystemFile&);  // записать в бинарный файл
      Fract operator[](int);  // оператор [] - доступ к дроби по номеру
      void print();
    };

    При необходимости добавить функций в класс SystemFile. Исключить однофременный доступ двух и более процессов к одной области файла.

    -- TatyanaOvsyannikova2011 - 20 Oct 2016