This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch master
in repository efm2.
View the commit online.
commit de7f9a21a3bb96f61ec0787f919d3b24a59e668f
Author: Carsten Haitzler (Rasterman) <ras...@rasterman.com>
AuthorDate: Thu Apr 11 10:01:45 2024 +0100
more work on mv - file count scan, status infra etc
also clean up some #includes we dont need... get this into git so it
isnt lost
---
src/backends/default/meta.c | 1 -
src/backends/default/mv.c | 183 ++++++++++++++++++++++-------
src/backends/default/open.c | 1 -
src/backends/default/status.c | 266 ++++++++++++++++++++++++++++++++++++++++++
src/backends/default/status.h | 32 +++++
src/efm/efm_graph.c | 1 -
6 files changed, 440 insertions(+), 44 deletions(-)
diff --git a/src/backends/default/meta.c b/src/backends/default/meta.c
index 6ac8ce8..46fddb9 100644
--- a/src/backends/default/meta.c
+++ b/src/backends/default/meta.c
@@ -4,7 +4,6 @@
#include <Ecore.h>
#include <Ecore_File.h>
#include <fcntl.h>
-#include "eina_types.h"
#include "sha.h"
#include "meta.h"
diff --git a/src/backends/default/mv.c b/src/backends/default/mv.c
index abc9873..bda92dd 100644
--- a/src/backends/default/mv.c
+++ b/src/backends/default/mv.c
@@ -5,35 +5,163 @@
#include <Efreet_Mime.h>
#include <Eet.h>
-#include <asm-generic/errno-base.h>
#include <asm-generic/errno.h>
+#include <errno.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
+#include <unistd.h>
-#include "cmd.h"
#include "eina_strbuf.h"
+#include "eina_thread.h"
#include "eina_types.h"
-#include "sha.h"
#include "meta.h"
#include "status.h"
static const char *config_dir = NULL;
+// this scans a tree to build a potential operation progress count. it may
+// not be 100% right as the fs can change while the scan happens and after
+// so any move that devolves into a cp + rm isn't going to be atomic and
+// handle a changing fs while it works anyway
+static Eina_Bool
+fs_scan(const char *src)
+{
+ Eina_Iterator *it;
+ const char *s;
+
+ if (strlen(src) < 1) return EINA_FALSE;
+ struct stat st;
+
+ if (lstat(src, &st) != 0)
+ {
+ switch (errno)
+ {
+ case EACCES:
+ status_error(src, NULL, "Move - Permission denied for source");
+ return EINA_FALSE;
+ break;
+ case EFAULT:
+ status_error(src, NULL, "Move - Memory Fault");
+ return EINA_FALSE;
+ break;
+ case ELOOP:
+ status_error(src, NULL, "Move - Too many symlinks");
+ return EINA_FALSE;
+ break;
+ case ENAMETOOLONG:
+ status_error(src, NULL, "Move - Name too long");
+ return EINA_FALSE;
+ break;
+ case ENOENT: // ignore this error - file removed during scan ?
+ return EINA_TRUE;
+ break;
+ case ENOMEM:
+ status_error(src, NULL, "Move - Out of memory");
+ return EINA_FALSE;
+ break;
+ case ENOTDIR:
+ status_error(src, NULL, "Move - Source path component is not a directory");
+ return EINA_FALSE;
+ break;
+ case EOVERFLOW:
+ status_error(src, NULL, "Move - Overflow");
+ return EINA_FALSE;
+ break;
+ default:
+ break;
+ }
+ }
+ if (S_ISDIR(st.st_mode))
+ { // it's a dir - scan this recursively
+ it = eina_file_ls(src);
+ if (it)
+ {
+ Eina_Strbuf *buf;
+
+ buf = eina_strbuf_new();
+ EINA_ITERATOR_FOREACH(it, s)
+ {
+ // build new path "src/s"
+ eina_strbuf_reset(buf);
+ eina_strbuf_append(buf, src);
+ // add / if src doesn't end in /
+ if (src[strlen(src) - 1] != '/') eina_strbuf_append(buf, "/");
+ eina_strbuf_append(buf, s);
+ eina_stringshare_del(s);
+ // fs_scan this file (or dir)
+ // yes - this returns. we make leak our eina_file_ls data, strbuf etc.
+ // dont care - we're going to exit the whole process in a bit
+ if (!fs_scan(eina_strbuf_string_get(buf))) return EINA_FALSE;
+ }
+ eina_strbuf_free(buf);
+ }
+ }
+ else
+ {
+ // the file itself count as 1 progress item - useful if it's a symlink
+ // or a char or block device etc.
+ status_count(1, ecore_file_file_get(src));
+ // in addition each byte in the file count as a progress item too
+ if (st.st_size > 0)
+ status_count(st.st_size, ecore_file_file_get(src));
+ }
+ return EINA_TRUE;
+}
+
+static Eina_Bool
+fs_cp_rm(const char *src, const char *dst, Eina_Bool report_err)
+{
+ Eina_Bool res = EINA_TRUE;
+
+ if (!fs_scan(src)) return EINA_FALSE;
+
+ // if src is dir
+ // fs_mkdir(dst)
+ // for all files in src
+ // fs_cp_rm(src/file, dst/file, report_err)
+ // fs_utimes(src, dst)
+ // fs_chmod(src, dst)
+ // fs_chown(src, dst)
+ // fs_utimes(src, dst)
+ // fs_chmod(src, dst)
+ // fs_chown(src, dst)
+ // fs_rmdir(src)
+ // else
+ // if (src is file)
+ // fs_cp(src, dst, report_err)
+ // fs_cp_xattr(src, dst)
+ // else if (src is symlink)
+ // fs_ln(src, dst)
+ // else if (src is fifo)
+ // fs_mkfifo(dst)
+ // else if (src is socket)
+ // fs_touch(dst)
+ // else if (src is chrdev)
+ // fs_chrdevdup(src, dst)
+ // else if (src is blkdev)
+ // fs_blkdevdup(src, dst)
+ // fs_utimes(src, dst)
+ // fs_chmod(src, dst)
+ // fs_chown(src, dst)
+ // fs_rm(src)
+ // fs_cp(src_meta, dst_meta)
+ // fs_utimes(src_meta, dst_meta)
+ // fs_chmod(src_meta, dst_meta)
+ // fs_chown(src_meta, dst_meta)
+ // fs_rm(src_meta)
+ return res;
+}
+
static Eina_Bool
fs_mv(const char *src, const char *dst, Eina_Bool report_err)
{
- // void status_begin(void);
- // void status_end(void);
- // void status_count(unsigned long long num_inc, const char *str);
- // void status_pos(unsigned long long pos_inc, const char *str));
- // void status_error(const char *path, const char *str);
Eina_Bool res = EINA_TRUE;
int ret;
- // XXX: write out status file and update it
// XXX: ecore_file_mv() ? <- look at it
// XXX: utime()/utimes() -> utimensat()
+ status_op("mv");
status_count(1, src);
ret = rename(src, dst);
if (ret == 0) return res;
@@ -117,10 +245,7 @@ fs_mv(const char *src, const char *dst, Eina_Bool report_err)
int
main(int argc, char **argv)
-{
- // mv [src] [dst] if (report_error) status_error(src, dst, "Move - Too
- // many symlinks");
-
+{ // mv [src] [dst]
const char *src, *dst, *fname, *home_dir;
Eina_Strbuf *buf = NULL;
@@ -156,27 +281,10 @@ main(int argc, char **argv)
// mkdir DDIR/.efm
// mv SDIR/.efm/SFILE.efm DDIR/.efm/SFILE.efm
- // status in
- // EFMDIR/status/uuid.status
- //
- // status file:
- // uuid (UUID) # uuid repeated from filename
- // pid (PID) @ pid of task
- // type (operation type - mv, cp, rm, ...) # op type
- // busy (0/1) # show a busy spinner instead of progress
- // progress (0-100) # show a progress bar 0-100 percent
- // title (some title string to show)
- // label (somt string to show) # show this string
- // icon (some icon name or full path to file)
- // ---
- //
- // check /proc/PID/cmdline so argv[1] is same uuid
- // write status in one write() as it'll be less than 4k
-
fname = ecore_file_file_get(src);
if (!fname) goto err;
buf = eina_strbuf_new();
- // XXX: if buf is null?
+ if (!buf) goto err;
eina_strbuf_append(buf, dst);
eina_strbuf_append(buf, "/");
eina_strbuf_append(buf, fname);
@@ -187,8 +295,6 @@ main(int argc, char **argv)
const char *dstfile;
char *src_meta, *dst_meta;
- fprintf(stderr, "MV: OK\n");
-
dstfile = eina_strbuf_string_get(buf);
src_can_write = meta_path_can_write(src);
@@ -199,10 +305,7 @@ main(int argc, char **argv)
if (dst_can_write) dst_meta = meta_path_find(dstfile, "meta.efm");
else dst_meta = meta_path_user_find(dstfile, "meta.efm");
if ((src_meta) && (dst_meta) && (meta_path_prepare(dstfile)))
- {
- // XXX: use mv() rename wrapper than will cp
- fs_mv(src_meta, dst_meta, EINA_FALSE);
- }
+ fs_mv(src_meta, dst_meta, EINA_FALSE);
free(src_meta);
free(dst_meta);
if (src_can_write) src_meta = meta_path_find(src, "thumb.efm");
@@ -210,13 +313,11 @@ main(int argc, char **argv)
if (dst_can_write) dst_meta = meta_path_find(dstfile, "thumb.efm");
else dst_meta = meta_path_user_find(dstfile, "thumb.efm");
if ((src_meta) && (dst_meta) && (meta_path_prepare(dstfile)))
- {
- // XXX: use mv() rename wrapper than will cp
- fs_mv(src_meta, dst_meta, EINA_FALSE);
- }
+ fs_mv(src_meta, dst_meta, EINA_FALSE);
free(src_meta);
free(dst_meta);
}
+ else pause(); // wait here - status thread will exit for us
err:
if (buf) eina_strbuf_free(buf);
status_end();
diff --git a/src/backends/default/open.c b/src/backends/default/open.c
index 531ccb6..6e97f87 100644
--- a/src/backends/default/open.c
+++ b/src/backends/default/open.c
@@ -18,7 +18,6 @@
#include <grp.h>
#include "cmd.h"
-#include "eina_strbuf.h"
#include "sha.h"
#include "meta.h"
#include "thumb_check.h"
diff --git a/src/backends/default/status.c b/src/backends/default/status.c
new file mode 100644
index 0000000..aff152e
--- /dev/null
+++ b/src/backends/default/status.c
@@ -0,0 +1,266 @@
+// enable eina_vpath ...
+#define EFL_BETA_API_SUPPORT 1
+// for vasprintf()
+#define _GNU_SOURCE
+#include "status.h"
+#include <Eina.h>
+#include <Ecore_File.h>
+#include <fcntl.h>
+
+typedef enum
+{
+ MSG_OP,
+ MSG_COUNT,
+ MSG_POS,
+ MSG_ERR,
+ MSG_END
+} Msg_Type;
+
+typedef struct
+{
+#define MSG_HEAD \
+ Eina_Thread_Queue_Msg head; \
+ Msg_Type type
+ MSG_HEAD;
+ void *dummy;
+} Msg;
+
+typedef struct
+{
+ MSG_HEAD;
+ char *op;
+} Msg_Op;
+
+typedef struct
+{
+ MSG_HEAD;
+ unsigned long long inc;
+ char *str;
+} Msg_Count;
+
+typedef struct
+{
+ MSG_HEAD;
+ unsigned long long inc;
+ char *str;
+} Msg_Pos;
+
+typedef struct
+{
+ MSG_HEAD;
+} Msg_End;
+
+typedef struct
+{
+ MSG_HEAD;
+ char *src;
+ char *dst;
+ char *str;
+} Msg_Err;
+
+static unsigned long long op_count = 0;
+static unsigned long long op_pos = 0;
+static unsigned long long op_count_prev = 0;
+static unsigned long long op_pos_prev = 0;
+static Eina_Bool op_end = EINA_FALSE;
+static char *op_str = NULL;
+static int status_fd = -1;
+static char *status_file = NULL;
+static Eina_Thread status_thread;
+static Eina_Thread_Queue *status_thq = NULL;
+
+static void
+_fd_printf(int fd, const char *fmt, ...)
+{
+ va_list args;
+ char *str;
+ size_t len;
+
+ va_start(args, fmt);
+ len = vasprintf(&str, fmt, args);
+ va_end(args);
+
+ if ((len == 0) || (!str)) return;
+ write(fd, str, len);
+ free(str);
+}
+
+static void
+_status_flush(void)
+{
+ if ((op_count == op_count_prev) && (op_pos == op_pos_prev)) return;
+ op_count_prev = op_count;
+ op_pos_prev = op_pos;
+ _fd_printf(status_fd, "CMD progress pos=%llu count=%llu\n", op_pos,
+ op_count);
+ if (op_str) _fd_printf(status_fd, "CMD progress str=%s\n", op_str);
+ if (op_end) _fd_printf(status_fd, "CMD end\n");
+ free(op_str);
+ op_str = NULL;
+}
+
+static void *
+_cb_status_thread(void *data EINA_UNUSED, Eina_Thread t EINA_UNUSED)
+{
+ Msg *msg;
+ void *ref;
+
+ for (;;)
+ { // loop and poll every 100ms (0.1s) for messages
+ msg = eina_thread_queue_poll(status_thq, &ref);
+ if (!msg)
+ {
+ // flush out write of last state - rate limit to 100ms
+ _status_flush();
+ usleep(100 * 1000); // wait for 100ms if no messages
+ continue;
+ }
+ switch (msg->type)
+ {
+ case MSG_OP:
+ {
+ Msg_Op *msg2 = (Msg_Op *)msg;
+ // write this out immediately
+ _fd_printf(status_fd, "CMD op %s\n", msg2->op);
+ _fd_printf(status_fd, "CMD pid %i\n", (int)getpid());
+ free(msg2->op);
+ }
+ break;
+ case MSG_COUNT:
+ {
+ Msg_Count *msg2 = (Msg_Count *)msg;
+ op_count += msg2->inc;
+ free(op_str);
+ op_str = msg2->str;
+ }
+ break;
+ case MSG_POS:
+ {
+ Msg_Pos *msg2 = (Msg_Pos *)msg;
+ op_pos += msg2->inc;
+ free(op_str);
+ op_str = msg2->str;
+ }
+ break;
+ case MSG_ERR:
+ {
+ Msg_Err *msg2 = (Msg_Err *)msg;
+ // write this out immediately
+ _fd_printf(status_fd, "CMD error err=%s\n", msg2->str);
+ if (msg2->src) _fd_printf(status_fd, "CMD error src="" msg2->src);
+ if (msg2->dst) _fd_printf(status_fd, "CMD error dst=%s\n", msg2->dst);
+ _fd_printf(status_fd, "CMD error done\n");
+ free(msg2->src);
+ free(msg2->dst);
+ free(msg2->str);
+ eina_thread_queue_wait_done(status_thq, ref);
+ exit(1);
+ }
+ break;
+ case MSG_END:
+ {
+ if (status_fd >= 0)
+ {
+ close(status_fd);
+ status_fd = -1;
+ }
+ if (status_file)
+ {
+ unlink(status_file);
+ free(status_file);
+ status_file = NULL;
+ }
+ eina_thread_queue_wait_done(status_thq, ref);
+ return NULL;
+ }
+ break;
+ default:
+ break;
+ }
+ eina_thread_queue_wait_done(status_thq, ref);
+ }
+ return NULL;
+}
+
+void
+status_begin(void)
+{
+ Eina_Bool ok;
+ char buf[PATH_MAX];
+
+ op_count = op_pos = 0;
+ eina_vpath_resolve_snprintf(buf, sizeof(buf), "(:usr.run:)/efm/ops");
+ ecore_file_mkpath(buf);
+ eina_vpath_resolve_snprintf(buf, sizeof(buf), "(:usr.run:)/efm/ops/%i.status",
+ (int)getpid());
+ status_file = strdup(buf);
+ status_fd = open(status_file, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
+ status_thq = eina_thread_queue_new();
+ ok = eina_thread_create(&status_thread, EINA_THREAD_BACKGROUND, -1,
+ _cb_status_thread, NULL);
+ if (!ok) abort();
+}
+
+void
+status_end(void)
+{
+ Msg_End *msg;
+ void *ref;
+
+ msg = eina_thread_queue_send(status_thq, sizeof(Msg_End), &ref);
+ msg->type = MSG_END;
+ eina_thread_queue_send_done(status_thq, ref);
+ eina_thread_join(status_thread);
+}
+
+void
+status_op(const char *op)
+{
+ Msg_Op *msg;
+ void *ref;
+
+ msg = eina_thread_queue_send(status_thq, sizeof(Msg_Op), &ref);
+ msg->type = MSG_OP;
+ msg->op = op ? strdup(op) : NULL;
+ eina_thread_queue_send_done(status_thq, ref);
+}
+
+void
+status_count(unsigned long long num_inc, const char *str)
+{
+ Msg_Count *msg;
+ void *ref;
+
+ msg = eina_thread_queue_send(status_thq, sizeof(Msg_Count), &ref);
+ msg->type = MSG_COUNT;
+ msg->inc = num_inc;
+ msg->str = str ? strdup(str) : NULL;
+ eina_thread_queue_send_done(status_thq, ref);
+}
+
+void
+status_pos(unsigned long long pos_inc, const char *str)
+{
+ Msg_Pos *msg;
+ void *ref;
+
+ msg = eina_thread_queue_send(status_thq, sizeof(Msg_Pos), &ref);
+ msg->type = MSG_POS;
+ msg->inc = pos_inc;
+ msg->str = str ? strdup(str) : NULL;
+ eina_thread_queue_send_done(status_thq, ref);
+}
+
+void
+status_error(const char *src, const char *dst, const char *str)
+{
+ Msg_Err *msg;
+ void *ref;
+
+ msg = eina_thread_queue_send(status_thq, sizeof(Msg_Err), &ref);
+ msg->type = MSG_ERR;
+ msg->src = "" ? strdup(src) : NULL;
+ msg->dst = dst ? strdup(dst) : NULL;
+ msg->str = str ? strdup(str) : NULL;
+ eina_thread_queue_send_done(status_thq, ref);
+}
diff --git a/src/backends/default/status.h b/src/backends/default/status.h
new file mode 100644
index 0000000..4fd21b2
--- /dev/null
+++ b/src/backends/default/status.h
@@ -0,0 +1,32 @@
+#ifndef STATUS_H
+#define STATUS_H
+#include <Eina.h>
+
+// status reporting writes status to a log file USERRUNTIME/efm/ops/PID.status
+// where USERRUNTIME might for example be /var/run/user/UID .
+//
+// this will be rate limited and thus not write a line for every change but
+// the last line or few lines in the file will be the latest status with a
+// small time lag so can be relied on to monitor and read new lines from
+// as they get appended and then display as needed. if everything succeeds
+// and the op completes the file will be delted. if there is an error, the
+// file is left there with the last few lines having some error information
+// so this can be displayed to the user
+//
+// lines in the status file are of the format
+// CMD command [arg1] [arg2] ...
+// where the first 4 chars are "CMD " and then a non-whitespace containing
+// command string, then possibly a space char and then arguments until a
+// a newline "\n" char for end of line. arguments mabay just be a set of
+// whitespace delimited words (so a string until the newline) or perhaps
+// of the form key=value depending on the command
+
+void status_begin(void);
+void status_end(void);
+void status_op(const char *op);
+void status_count(unsigned long long num_inc, const char *str);
+void status_pos(unsigned long long pos_inc, const char *str);
+// this call will cause the status thread to report the error then exit(1)
+void status_error(const char *src, const char *dst, const char *str);
+
+#endif
\ No newline at end of file
diff --git a/src/efm/efm_graph.c b/src/efm/efm_graph.c
index 5b3bc50..c3ba5e7 100644
--- a/src/efm/efm_graph.c
+++ b/src/efm/efm_graph.c
@@ -1,5 +1,4 @@
#include "efm_graph.h"
-#include "eina_stringshare.h"
typedef struct _Smart_Data Smart_Data;
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.