Exemple #1
0
void
json_print_status_line(struct status_line *status)
{
	bool first = true;
	int i = 0;

	fprintf(stdout, ",[");

	for (i = 0; i < status->num; ++i) {
		struct block *block = status->updated_blocks + i;

		/* full_text is the only mandatory key, skip if empty */
		if (!*block->full_text) {
			bdebug(block, "no text to display, skipping");
			continue;
		}

		if (!first) fprintf(stdout, ",");
		else first = false;

		bdebug(block, "print json");
		block_to_json(block);
	}

	fprintf(stdout, "]\n");
	fflush(stdout);
}
Exemple #2
0
void
block_reap(struct block *block)
{
	int status, code;

	if (block->pid <= 0) {
		bdebug(block, "not spawned yet");
		return;
	}

	if (waitpid(block->pid, &status, 0) == -1) {
		berrorx(block, "waitpid(%d)", block->pid);
		mark_as_failed(block, strerror(errno));
		goto close;
	}

	code = WEXITSTATUS(status);
	bdebug(block, "process %d exited with %d", block->pid, code);

	/* Process successfully reaped, reset the block PID */
	block->pid = 0;

	block_dump_stderr(block);

	if (code != 0 && code != EXIT_URGENT) {
		char reason[32];

		if (code == EXIT_ERR_INTERNAL)
			sprintf(reason, "internal error");
		else
			sprintf(reason, "bad exit code %d", code);

		berror(block, "%s", reason);
		mark_as_failed(block, reason);
		goto close;
	}

	/* Do not update unless it was meant to terminate */
	if (block->interval == INTER_PERSIST)
		goto close;

	block_update(block);

	/* Exit code takes precedence over the output */
	if (code == EXIT_URGENT)
		strcpy(block->updated_props.urgent, "true");
close:
	if (close(block->out) == -1)
		berrorx(block, "close stdout");
	if (close(block->err) == -1)
		berrorx(block, "close stderr");
}
Exemple #3
0
static int
parse_status_line(FILE *fp, struct status_line *status)
{
	struct block *block = NULL;
	struct block global;
	char line[2048];

	memset(&global, 0, sizeof(struct block));

	while (fgets(line, sizeof(line), fp) != NULL) {
		int len = strlen(line);

		if (line[len - 1] != '\n') {
			error("line \"%s\" is not terminated by a newline", line);
			return 1;
		}
		line[len - 1] = '\0';

		switch (*line) {
		/* Comment or empty line? */
		case '#':
		case '\0':
			break;

		/* Section? */
		case '[':
			block = add_block(status);
			if (!block)
				return 1;

			/* Init the block with default settings (if any) */
			memcpy(block, &global, sizeof(struct block));

			if (parse_section(line, block->name, sizeof(block->name)))
				return 1;

			bdebug(block, "new block");
			break;

		/* Property? */
		case 'a' ... 'z':
			if (!block) {
				debug("no section yet, consider global properties");
				block = &global;
			}

			if (parse_property(line, block))
				return 1;

			break;

		/* Syntax error */
		default:
			error("malformated line: %s", line);
			return 1;
		}
	}

	return duplicate_blocks(status);
}
Exemple #4
0
static inline bool
need_update(struct block *block)
{
	bool first_time, outdated, signaled, clicked;

	first_time = outdated = signaled = clicked = false;

	if (block->last_update == 0)
		first_time = true;

	if (block->interval) {
		const unsigned long now = time(NULL);
		const unsigned long next_update = block->last_update + block->interval;

		outdated = ((long) (next_update - now)) <= 0;
	}

	if (caughtsig) {
		signaled = caughtsig == block->signal;
		clicked = *block->click.button != '\0';
	}

	bdebug(block, "CHECK first_time: %s, outdated: %s, signaled: %s, clicked: %s",
			first_time ? "YES" : "no",
			outdated ? "YES" : "no",
			signaled ? "YES" : "no",
			clicked ? "YES" : "no");

	return first_time || outdated || signaled || clicked;
}
Exemple #5
0
static void
update_status_line(struct status_line *status)
{
	int i;

	for (i = 0; i < status->num; ++i) {
		const struct block *config_block = status->blocks + i;
		struct block *updated_block = status->updated_blocks + i;

		/* Skip static block */
		if (!*config_block->command) {
			bdebug(config_block, "no command, skipping");
			continue;
		}

		/* If a block needs an update, reset and execute it */
		if (need_update(updated_block)) {
			struct click click;

			/* save click info and restore config values */
			memcpy(&click, &updated_block->click, sizeof(struct click));
			memcpy(updated_block, config_block, sizeof(struct block));
			memcpy(&updated_block->click, &click, sizeof(struct click));

			block_update(updated_block);

			/* clear click info */
			memset(&updated_block->click, 0, sizeof(struct click));
		}
	}

	if (caughtsig > 0)
		caughtsig = 0;
}
Exemple #6
0
static void
handle_click(struct status_line *status)
{
	char json[1024] = { 0 };
	struct click click = { "" };
	char *name, *instance;

	fread(json, 1, sizeof(json) - 1, stdin);

	parse_click(json, &name, &instance, &click);
	debug("got a click: name=%s instance=%s button=%s x=%s y=%s",
			name, instance, click.button, click.x, click.y);

	/* find the corresponding block */
	if (*name || *instance) {
		int i;

		for (i = 0; i < status->num; ++i) {
			struct block *block = status->updated_blocks + i;

			if (strcmp(block->name, name) == 0 && strcmp(block->instance, instance) == 0) {
				memcpy(&block->click, &click, sizeof(struct click));

				/* It shouldn't be likely to have several blocks with the same name/instance, so stop here */
				bdebug(block, "clicked");
				break;
			}
		}
	}
}
Exemple #7
0
void bwrite(buffer *buff) {

    debug(BUFFER_DL, "bwrite(buff:%p) {\n", buff);

    // mark to be written
    BS_SET(buff->status, BS_DELAYED);

    bdebug(1 + BUFFER_DL, buff);

    debug(BUFFER_DL, "}\n");
}
Exemple #8
0
void
block_update(struct block *block)
{
	FILE *child_stdout;
	int child_status, code;
	char output[2048], *text = output;

	if (setup_env(block))
		return mark_as_failed(block, "failed to setup env", -1);

	/* Pipe, fork and exec a shell for the block command line */
	child_stdout = popen(block->command, "r");
	if (!child_stdout) {
		berrorx(block, "popen(%s)", block->command);
		return mark_as_failed(block, "failed to fork", -1);
	}

	/* Do not distinguish EOF or error, just read child's output */
	memset(output, 0, sizeof(output));
	fread(output, 1, sizeof(output) - 1, child_stdout);

	/* Wait for the child process to terminate */
	child_status = pclose(child_stdout);
	if (child_status == -1) {
		berrorx(block, "pclose");
		return mark_as_failed(block, "failed to wait", -1);
	}

	if (!WIFEXITED(child_status)) {
		berror(block, "child did not exit correctly");
		return mark_as_failed(block, "command did not exit", -1);
	}

	code = WEXITSTATUS(child_status);
	if (code != 0 && code != '!') {
		char reason[1024] = { 0 };

		berror(block, "bad exit code %d", code);
		linecpy(&text, reason, sizeof(reason) - 1);
		return mark_as_failed(block, reason, code);
	}

	/* From here, the update went ok so merge the output */
	strncpy(block->urgent, code == '!' ? "true" : "false", sizeof(block->urgent) - 1);
	linecpy(&text, block->full_text, sizeof(block->full_text) - 1);
	linecpy(&text, block->short_text, sizeof(block->short_text) - 1);
	linecpy(&text, block->color, sizeof(block->color) - 1);
	block->last_update = time(NULL);

	bdebug(block, "updated successfully");
}
Exemple #9
0
static void
block_dump_stderr(struct block *block)
{
	char buf[2048] = { 0 };

	/* Note read(2) returns 0 for end-of-pipe */
	if (read(block->err, buf, sizeof(buf) - 1) == -1) {
		berrorx(block, "read stderr");
		return;
	}

	if (*buf)
		bdebug(block, "stderr:\n{\n%s\n}", buf);
}
Exemple #10
0
static int
parse_property(const char *line, struct block *block)
{
	char *equal = strchr(line, '=');
	const char *property, *value;

	if (!equal) {
		berror(block, "malformated property, should be of the form 'key=value'");
		return 1;
	}

	/* split property and value */
	*equal = '\0';
	property = line;
	value = equal + 1;

#define PARSE(_name, _size, _type) \
	if (strcmp(property, #_name) == 0) { \
		strncpy(block->_name, value, _size - 1); \
		goto parsed; \
	} \

#define PARSE_NUM(_name) \
	if (strcmp(property, #_name) == 0) { \
		block->_name = atoi(value); \
		goto parsed; \
	} \

	PROTOCOL_KEYS(PARSE);
	PARSE(command, sizeof(block->command), _);
	PARSE_NUM(interval);
	PARSE_NUM(signal);
	/* TODO some better check for numbers and boolean */

#undef PARSE_NUM
#undef PARSE

	berror(block, "unknown property: \"%s\"", property);
	return 1;

parsed:
	bdebug(block, "set property %s to \"%s\"", property, value);
	return 0;
}
Exemple #11
0
void
block_update(struct block *block)
{
	struct properties *props = &block->updated_props;
	char buf[2048] = { 0 };
	int nr;

	/* Read a single line for persistent block, everything otherwise */
	if (block->interval == INTER_PERSIST) {
		nr = io_readline(block->out, buf, sizeof(buf));
		if (nr < 0) {
			berror(block, "failed to read a line");
			return mark_as_failed(block, "failed to read");
		} else if (nr == 0) {
			berror(block, "pipe closed");
			return mark_as_failed(block, "pipe closed");
		}
	} else {
		/* Note: read(2) returns 0 for end-of-pipe */
		if (read(block->out, buf, sizeof(buf) - 1) == -1) {
			berrorx(block, "read stdout");
			return mark_as_failed(block, strerror(errno));
		}
	}

	/* Reset the defaults and merge the output */
	memcpy(props, &block->default_props, sizeof(struct properties));

	if (block->format == FORMAT_JSON)
		block_update_json(block, buf);
	else
		block_update_plain_text(block, buf);

	if (*FULL_TEXT(block) && *LABEL(block)) {
		static const size_t size = sizeof(props->full_text);
		char concat[size];
		snprintf(concat, size, "%s %s", LABEL(block), FULL_TEXT(block));
		strcpy(props->full_text, concat);
	}

	bdebug(block, "updated successfully");
}
Exemple #12
0
void
json_print_bar(struct bar *bar)
{
	fprintf(stdout, ",[{\"full_text\":\"\"}");

	for (int i = 0; i < bar->num; ++i) {
		struct block *block = bar->blocks + i;

		/* full_text is the only mandatory key, skip if empty */
		if (!*FULL_TEXT(block)) {
			bdebug(block, "no text to display, skipping");
			continue;
		}

		print_block(block);
	}

	fprintf(stdout, "]\n");
	fflush(stdout);
}
Exemple #13
0
buffer *bread(int fs, ino_t inode, int block) {

    debug(BUFFER_DL, "bread(fs:%d, inode:%d, block:%d) {\n", fs, (int)inode, block);

    buffer *buff = getblk(fs, inode, block);

    if (!BS_CMP(buff->status, BS_VALID)) {
        int s = lseek(buff->fd, buff->block * BLKSIZE, SEEK_SET);

        buff->valid = read(buff->fd, buff->data, BLKSIZE);
        if (buff->valid == -1)
            perror("No se pudo leer el archivo");

        BS_SET(buff->status, BS_VALID);
    }


    bdebug(1 + BUFFER_DL, buff);

    debug(BUFFER_DL, "} -> %p\n", buff);

    return buff;
}
Exemple #14
0
void
block_spawn(struct block *block, struct click *click)
{
	const unsigned long now = time(NULL);
	int out[2], err[2];

	if (!*COMMAND(block)) {
		bdebug(block, "no command, skipping");
		return;
	}

	if (block->pid > 0) {
		bdebug(block, "process already spawned");
		return;
	}

	if (pipe(out) == -1 || pipe(err) == -1) {
		berrorx(block, "pipe");
		return mark_as_failed(block, strerror(errno));
	}

	if (block->interval == INTER_PERSIST) {
		if (io_signal(out[0], SIGRTMIN))
			return mark_as_failed(block, "event I/O impossible");
	}

	block->pid = fork();
	if (block->pid == -1) {
		berrorx(block, "fork");
		return mark_as_failed(block, strerror(errno));
	}

	/* Child? */
	if (block->pid == 0) {
		/* Error messages are merged into the parent's stderr... */
		child_setup_env(block, click);
		child_reset_signals(block);
		child_redirect_write(block, out, STDOUT_FILENO);
		child_redirect_write(block, err, STDERR_FILENO);
		/* ... until here */
		child_exec(block);
	}

	/*
	 * Note: for non-persistent blocks, no need to set the pipe read end as
	 * non-blocking, since it is meant to be read once the child has exited
	 * (and thus the write end is closed and read is available).
	 */

	/* Parent */
	if (close(out[1]) == -1)
		berrorx(block, "close stdout");
	if (close(err[1]) == -1)
		berrorx(block, "close stderr");

	block->out = out[0];
	block->err = err[0];

	if (!click)
		block->timestamp = now;

	bdebug(block, "forked child %d at %ld", block->pid, now);
}
Exemple #15
0
buffer *getblk(int fs, ino_t inode, int block) {

    debug(1 + BUFFER_DL, "getblk(fs:%d, inode:%d, block:%d) {\n", fs, (int)inode, block);

    // wait for semaphore to start
    while (!semWait(semid)) {
        // skip signal interruptions
        if (errno != EINTR) {
            perror("getblk, semWait");
            debug(2 + BUFFER_DL, "ERROR en getblk, semWait.");
            exit(1);
        }
    }

    // search for specified buffer or return a new one
    buffer *buff = NULL;
    while (buff == NULL) {
        // search hash list for block
        int pos = bc->header.hash[block % MAXHASH];

        buffer *hl_buff = (pos > -1) ? &(bc->buffers[pos]) : NULL;
        int times = 0;
        while(pos > -1 && (hl_buff->inode != inode || hl_buff->block != block)) {
            times ++;
            if (times == MAXBUFF) {
                printf("[%d] times: %d ... \n", getpid(), times);

                int end = pos;
                do {
                    bdebug(0, &(bc->buffers[pos]));
                    pos = bc->buffers[pos].hashnext;
                } while (end != pos);

                exit(0);
            }

            pos = hl_buff->hashnext;

            hl_buff = (pos > -1) ? &(bc->buffers[pos]) : NULL;
        }

        // return the buffer if already loaded
        if (hl_buff != NULL && hl_buff->inode == inode && hl_buff->block == block) {

            // sleep until the targeted buffer becomes free
            if  (BS_CMP(hl_buff->status, BS_LOCKED)) {
                bwait(hl_buff);
                continue;
            }

            // take ownership off buffer
            hl_buff->fd = getfd(inode);
            hl_buff->pid = getpid();

            // lock the found buffer
            BS_SET(hl_buff->status, BS_LOCKED);

            // will return found buffer
            buff = hl_buff;

        // allocate the block in a new buffer
        } else {
            buffer *new_buff = getfree();

            // sleep until any buffer becomes free
            if (new_buff == NULL) {
                bwait(NULL);
                continue;
            }

            // asynchronous write buffer to disk
            while (BS_CMP(new_buff->status, BS_DELAYED)) {
                basyncwrite(new_buff);

                // wait for free buffer to be written
                usleep(1000);
            }

            // set new buffer properties and proper hash list
            drophash(new_buff);

            new_buff->valid = 0;
            new_buff->inode = inode;
            new_buff->block = block;
            new_buff->fd = getfd(inode);
            new_buff->pid = getpid();

            rehash(new_buff);

            // lock the new buffer and mark it as not valid
            BS_SET(new_buff->status, BS_LOCKED);
            BS_CLR(new_buff->status, BS_VALID);

            // will return the new buffer
            buff = new_buff;
        }
    }

    // extract the buffer from free list
    setused(buff);

    // free the semaphore
    if (!semSignal(semid)) {
        perror("getblk, semSignal");
        debug(2 + BUFFER_DL, "ERROR en getblk, semSignal.");
        exit(1);
    }

    bdebug(2 + BUFFER_DL, buff);

    debug(1 + BUFFER_DL, "} -> %p\n", buff);

    return buff;
}