mirror of
https://github.com/clearlinux/swupd-client.git
synced 2026-09-07 22:21:32 +00:00
236 lines
6.4 KiB
C
236 lines
6.4 KiB
C
/*
|
|
* Software Updater - client side
|
|
*
|
|
* Copyright © 2014-2016 Intel Corporation.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, version 2 or later of the License.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Authors:
|
|
* Tim Pepper <timothy.c.pepper@linux.intel.com>
|
|
* Bryce Patel <bryce.patel@intel.com>
|
|
*
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
#include <assert.h>
|
|
#include <sched.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#include "swupd.h"
|
|
#define PROTECTED_FILE "./test/protected_data"
|
|
#define NUM_THREADS 8
|
|
#define NUM_ACCESS 4
|
|
|
|
/* Attempt to get a write lock protecting the file test/protected_data using
|
|
* p_lockfile(). If successful, no two processess should be able to hold the
|
|
* lock similtaneously.
|
|
*/
|
|
static void work(int id)
|
|
{
|
|
int lock_fd;
|
|
int successes = 0;
|
|
|
|
FILE *file;
|
|
char buffer[LINE_MAX];
|
|
|
|
while (successes < NUM_ACCESS) {
|
|
int prev_id = -1;
|
|
char prev_action = ' ';
|
|
char c = ' ';
|
|
|
|
// Sleep for a random amount of time, up to half a second so
|
|
// not all processes attempt to access lock at once.
|
|
usleep(rand() % 500000);
|
|
|
|
// Attempt to access the lock
|
|
lock_fd = p_lockfile();
|
|
if (lock_fd < 0) {
|
|
printf("process %i unable to acquire lock\n", id);
|
|
fflush(stdout);
|
|
|
|
continue;
|
|
}
|
|
|
|
printf("process %i acquired lock\n", id);
|
|
|
|
file = fopen(PROTECTED_FILE, "a+");
|
|
|
|
fseek(file, 0, SEEK_END);
|
|
|
|
// Check if file is empty
|
|
if (ftell(file) != 0) {
|
|
// Go to start of last line
|
|
while (c != '\n' && fseek(file, -2, SEEK_CUR) == 0) {
|
|
c = getc(file);
|
|
}
|
|
|
|
// If the file only has one line, return to start of file
|
|
if (c != '\n') {
|
|
rewind(file);
|
|
}
|
|
|
|
// Read last line of protected_data
|
|
sscanf(fgets(buffer, LINE_MAX, file), "%i%c\n", &prev_id, &prev_action);
|
|
}
|
|
|
|
fprintf(file, "%ip\n", id);
|
|
fflush(file);
|
|
|
|
if (prev_action == 'p') {
|
|
printf("process %i accessed lock being held by process %i\n", id, prev_id);
|
|
printf("process %i aborting\n\n", id);
|
|
return;
|
|
}
|
|
|
|
// Sleep for up to a fifth of a second while holding lock. During this time
|
|
// other processes should try to access lock and fail. This sleep is shorter
|
|
// than the one before attempting to get the lock because we don't need to
|
|
// wait for all other processes to attempt to access lock, just some of them
|
|
usleep(rand() % 200000);
|
|
|
|
fseek(file, 0, SEEK_END);
|
|
|
|
if (ftell(file) == 0) {
|
|
printf("process %i failed to write to protected_data", id);
|
|
printf("process %i aborting\n\n", id);
|
|
} else {
|
|
char c = ' ';
|
|
while (c != '\n' && fseek(file, -2, SEEK_CUR) == 0) {
|
|
c = getc(file);
|
|
}
|
|
|
|
if (c != '\n') {
|
|
rewind(file);
|
|
}
|
|
|
|
sscanf(fgets(buffer, LINE_MAX, file), "%i%c\n", &prev_id, &prev_action);
|
|
}
|
|
|
|
// Log that this thread released the lock
|
|
fprintf(file, "%iv\n", id);
|
|
fclose(file);
|
|
|
|
if (prev_action != 'p' || prev_id != id) {
|
|
if (prev_id == -1) {
|
|
printf("process %i released lock without holding it\n", id);
|
|
} else {
|
|
printf("process %i and process %i held lock similtaneously\n", id, prev_id);
|
|
}
|
|
printf("process %i aborting\n\n", id);
|
|
return;
|
|
}
|
|
|
|
printf("process %i released lock\n\n", id);
|
|
v_lockfile(lock_fd);
|
|
successes++;
|
|
}
|
|
return;
|
|
}
|
|
|
|
int main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
|
|
{
|
|
int ret = EXIT_FAILURE;
|
|
int t_cnt;
|
|
pid_t pid[NUM_THREADS];
|
|
FILE *file;
|
|
int id, prev_id = -1;
|
|
char action, prev_action = ' ';
|
|
char buffer[LINE_MAX];
|
|
|
|
unlink(PROTECTED_FILE);
|
|
|
|
// Create multiple processess to attempt to access the lock
|
|
for (t_cnt = 0; t_cnt < NUM_THREADS; t_cnt++) {
|
|
pid[t_cnt] = fork();
|
|
|
|
// Child processes will execute work() then exit
|
|
if (pid[t_cnt] == 0) {
|
|
work(t_cnt);
|
|
return EXIT_SUCCESS;
|
|
}
|
|
}
|
|
|
|
// From here on is executed only by parent process
|
|
|
|
for (t_cnt = 0; t_cnt < NUM_THREADS; t_cnt++) {
|
|
wait(&ret);
|
|
}
|
|
|
|
file = fopen(PROTECTED_FILE, "r+");
|
|
if (file == NULL) {
|
|
printf("Log file could not be opened\n");
|
|
ret = -1;
|
|
}
|
|
|
|
printf("Test results:\n");
|
|
// Makes sure each time the lock is accessed, no other processes can access it until
|
|
// the process holding it released it.
|
|
while (fgets(buffer, LINE_MAX, file) != NULL && ret >= 0) {
|
|
sscanf(buffer, "%i%c\n", &id, &action);
|
|
if (prev_action == 'p') {
|
|
if (action == 'p') {
|
|
if (prev_id == id) {
|
|
// Happens when a process holding the lock accesses it again
|
|
// protected_data will look like "0p0p"
|
|
printf("process %i accessed lock twice without closing\n", id);
|
|
ret = -1;
|
|
} else {
|
|
// Happens when two processes hold the lock at the same time
|
|
// protected_data will look like "0p1p"
|
|
// In this case, lock is likely not locking properly
|
|
// If the lock isn't working, this is the most likely error message
|
|
printf("process %i accessed lock being held by process %i\n", id, prev_id);
|
|
ret = -1;
|
|
}
|
|
} else if (prev_id != id) {
|
|
// Happens when a lock being held by one process is closed by another process.
|
|
// protected_data will look like "0p1v"
|
|
// In this case, lock is likely not locking properly
|
|
printf("process %i released lock being held by process %i\n", id, prev_id);
|
|
ret = -1;
|
|
}
|
|
} else if (action == 'v') {
|
|
if (prev_action == 'v') {
|
|
// Happens when two processes hold the lock at the same time and then release it
|
|
// protected_data will look like "0v1v"
|
|
// In this case, lock is likely not locking properly
|
|
printf("process %i released lock after process %i released it, without accessing first\n", id, prev_id);
|
|
ret = -1;
|
|
} else if (prev_id == -1) {
|
|
// Happens if a lock is released without any process ever accessing it
|
|
// protected_data will start with something like "0v"
|
|
printf("process %i released lock without holding it\n", id);
|
|
ret = -1;
|
|
}
|
|
}
|
|
|
|
prev_action = action;
|
|
prev_id = id;
|
|
}
|
|
|
|
fclose(file);
|
|
|
|
if (ret >= 0) {
|
|
printf("Lock passed the test\n");
|
|
} else {
|
|
printf("Lock failed the test\n");
|
|
}
|
|
return ret;
|
|
}
|